<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/how-to-write-an-api-from-hell-or-how-to-send-a-bad-http-response-oktparg5a" -->

---
title: How to write an API From Hell or how to send a bad HTTP...
description: An open-source project called the API From Hell deliberately generates malformed HTTP responses (204/205/304 with bodies, missing Content-Type headers, BOMs,...
canonical: https://daily.dev/posts/how-to-write-an-api-from-hell-or-how-to-send-a-bad-http-response-oktparg5a
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: How to write an API From Hell or how to send a bad HTTP Response | daily.dev
og:description: An open-source project called the API From Hell deliberately generates malformed HTTP responses (204/205/304 with bodies, missing Content-Type headers, BOMs,...
og:url: https://daily.dev/posts/how-to-write-an-api-from-hell-or-how-to-send-a-bad-http-response-oktparg5a
og:image: https://api.daily.dev/og/posts/okTPaRg5A.png
og:image:alt: How to write an API From Hell or how to send a bad HTTP Response
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# How to write an API From Hell or how to send a bad HTTP Response

**[Evil Tester](https://daily.dev/sources/eviltester)** · 10 min read · 0 upvotes · 0 comments

## Summary

An open-source project called the API From Hell deliberately generates malformed HTTP responses (204/205/304 with bodies, missing Content-Type headers, BOMs, null bytes, control characters) to stress test HTTP clients, REST tools, and proxies. Building it required bypassing default framework behavior in Node/Express, Python/Flask, and Java/Javalin, often dropping to raw sockets to prevent frameworks from normalizing or stripping bad data. Testing against real tools revealed that Postman fails to render 204/304 bodies, MITMProxy and Zap strip content from those statuses, and Bruno silently 'fixes' broken XML - while BurpSuite was the only proxy that faithfully passed through the full set of hellish payloads.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://www.eviltester.com/blog/eviltester/apichallenges/2026-08-20-how-i-coded-api-from-hell>

## Questions this post answers

### Why do Postman and MITMProxy fail to show the body of a 204 or 304 HTTP response?

They strip content from 204, 205, and 304 responses because HTTP clients and proxies typically assume these status codes never carry a body, so they filter it out during processing. Postman does not render 204/304 messages when they contain content, and both MITMProxy and Zap strip that content before passing it downstream, unlike BurpSuite which proxies it correctly.

_Anyone debugging weird empty-body proxy behavior can compare tool quirks like this on daily.dev._

### How do you send an HTTP response with no Content-Type header when Flask keeps adding a default one?

Bypass Flask's default response handling by creating a custom Response subclass and explicitly removing the header, since flask.Response starts with default metadata that assigns a Content-Type even to plain bodies. An ExactResponse class was built that calls response.headers.pop("Content-Type", None) before sending, avoiding Werkzeug's automatic header normalization for special statuses.

_Developers hardening edge-case HTTP handling in Flask can track framework quirks like this via daily.dev._

### Which HTTP proxy tools correctly pass through malformed responses like 204s with a body?

BurpSuite is the only proxy tool confirmed to proxy the full set of hostile payloads and headers, including 204 and 304 responses with body content. Both MITMProxy and ZAP failed to proxy those payloads through and also failed to display the body within the proxy tool itself.

_Teams choosing a proxy for accurate traffic inspection can weigh these tradeoffs on daily.dev._

## Similar posts on daily.dev

- [API From Hell Shows Your API Client is Lying to you](https://daily.dev/posts/api-from-hell-shows-your-api-client-is-lying-to-you-ecjmkhtc3) · Evil Tester · 0 upvotes · 0 comments
- [What Is REST API Fuzz Testing?](https://daily.dev/posts/what-is-rest-api-fuzz-testing--kpwfzhwcq) · Nordic APIs · 0 upvotes · 0 comments
- [How to Test an API Without a Frontend](https://daily.dev/posts/how-to-test-an-api-without-a-frontend-5phbkfhlw) · Bruno Blog · 16 upvotes · 3 comments
- [REST API Monitoring Beyond Status Codes](https://daily.dev/posts/rest-api-monitoring-beyond-status-codes-4cgbpbtn4) · SitePoint · 0 upvotes · 0 comments

---

Tags: [#testing](https://daily.dev/tags/testing), [#flask](https://daily.dev/tags/flask)

[View this post on daily.dev](https://daily.dev/posts/how-to-write-an-api-from-hell-or-how-to-send-a-bad-http-response-oktparg5a)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"How to write an API From Hell or how to send a bad HTTP Response","url":"https://daily.dev/posts/how-to-write-an-api-from-hell-or-how-to-send-a-bad-http-response-oktparg5a","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/how-to-write-an-api-from-hell-or-how-to-send-a-bad-http-response-oktparg5a"},"datePublished":"2026-08-20T10:52:56.721Z","dateModified":"2026-09-14T07:55:18.774Z","description":"An open-source project called the API From Hell deliberately generates malformed HTTP responses (204/205/304 with bodies, missing Content-Type headers, BOMs,...","image":"https://media.daily.dev/image/upload/s--qPvKM23u--/f_auto/v1722860399/public/Placeholder%2009","thumbnailUrl":"https://media.daily.dev/image/upload/s--qPvKM23u--/f_auto/v1722860399/public/Placeholder%2009","isAccessibleForFree":true,"articleSection":"Evil Tester","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Evil Tester","logo":"https://media.daily.dev/image/upload/s--2ssxwmPY--/f_auto,q_auto/v1780213666/logos/eviltester?_a=BAMAMiWQ0","url":"https://daily.dev/sources/eviltester"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/how-to-write-an-api-from-hell-or-how-to-send-a-bad-http-response-oktparg5a","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"testing,flask","timeRequired":"PT10M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Evil Tester","item":"https://daily.dev/sources/eviltester"},{"@type":"ListItem","position":3,"name":"How to write an API From Hell or how to send a bad HTTP Response"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/how-to-write-an-api-from-hell-or-how-to-send-a-bad-http-response-oktparg5a#faq","mainEntity":[{"@type":"Question","name":"Why do Postman and MITMProxy fail to show the body of a 204 or 304 HTTP response?","acceptedAnswer":{"@type":"Answer","text":"They strip content from 204, 205, and 304 responses because HTTP clients and proxies typically assume these status codes never carry a body, so they filter it out during processing. Postman does not render 204/304 messages when they contain content, and both MITMProxy and Zap strip that content before passing it downstream, unlike BurpSuite which proxies it correctly. Anyone debugging weird empty-body proxy behavior can compare tool quirks like this on daily.dev."}},{"@type":"Question","name":"How do you send an HTTP response with no Content-Type header when Flask keeps adding a default one?","acceptedAnswer":{"@type":"Answer","text":"Bypass Flask's default response handling by creating a custom Response subclass and explicitly removing the header, since flask.Response starts with default metadata that assigns a Content-Type even to plain bodies. An ExactResponse class was built that calls response.headers.pop(\"Content-Type\", None) before sending, avoiding Werkzeug's automatic header normalization for special statuses. Developers hardening edge-case HTTP handling in Flask can track framework quirks like this via daily.dev."}},{"@type":"Question","name":"Which HTTP proxy tools correctly pass through malformed responses like 204s with a body?","acceptedAnswer":{"@type":"Answer","text":"BurpSuite is the only proxy tool confirmed to proxy the full set of hostile payloads and headers, including 204 and 304 responses with body content. Both MITMProxy and ZAP failed to proxy those payloads through and also failed to display the body within the proxy tool itself. Teams choosing a proxy for accurate traffic inspection can weigh these tradeoffs on daily.dev."}}]}
```

