Last week, I ran into one of those tricky production bugs where everything looked fine, but Azure Front Door kept throwing a 504 Gateway Timeout.
And the real cause? A simple missing HTTP method. 😅
⚙️ My Setup
I had a typical production setup:
-
FastAPI app running on port
8000as a background process (managed via systemd, so it auto-starts on reboot) - Nginx working as a reverse proxy, handling all HTTP/HTTPS traffic
- Everything deployed behind Azure Front Door
So the flow looked like this:
Frontend → Azure Front Door → Nginx → FastAPI (port 8000)
When I tested everything manually:
curl http://localhost:8000/health
✅ returned “healthy”
✅ app responded instantly
✅ SSL working fine via Nginx
✅ no errors in systemd logs
Still, Front Door said:
“No healthy backends available.”
And my frontend just sat there… until it failed with a 504 Gateway Timeout. 😩
🧠 The Clue I Almost Missed
While checking my logs, I noticed this repeating line every 30 seconds:
INFO: 127.0.0.1:44438 "HEAD /health HTTP/1.0" 405 Method Not Allowed
That was the “aha!” moment.
Azure Front Door wasn’t using GET at all — it was sending a HEAD /health request.
🧩 What Was Happening
Here’s what Front Door was doing under the hood:
Every 30 seconds:
Front Door → HEAD /health → backend VM
But my FastAPI code looked like this:
@app.get("/health")
async def health():
return {"status": "healthy"}
So my app said:
“I don’t accept that method.” → 405 Method Not Allowed ❌
Front Door took that as:
“Backend is unhealthy.”
And started marking every VM as unhealthy.
💥 The Domino Effect
HEAD /health → 405 ❌ → Backend unhealthy
All backends unhealthy → No route to send traffic
Front Door → Waits → Times out after 60s
Frontend → 504 Gateway Timeout
Meanwhile, when I ran a GET manually, everything looked healthy.
That’s what made this bug so sneaky.
🩹 The One-Line Fix
I updated my FastAPI route to accept both GET and HEAD:
from fastapi import FastAPI, Response
app = FastAPI()
@app.api_route("/health", methods=["GET", "HEAD"], include_in_schema=False)
async def health():
return Response(content="healthy", status_code=200)
Now both checks pass:
curl http://localhost:8000/health # GET → healthy ✅
curl -I http://localhost:8000/health # HEAD → 200 OK ✅
And Azure Front Door is happy again 💙
🔍 Why HEAD Is Used
HEAD is like a lightweight GET it only retrieves headers, no body.
It’s faster and cheaper to run for hundreds of backend checks.
That’s why all major load balancers (Azure Front Door, AWS ALB, GCP LB, Nginx) prefer using it for health probes.
🧠 TL;DR
| Problem | Explanation |
|---|---|
Azure Front Door used HEAD /health
|
Industry-standard health probe |
FastAPI accepted only GET
|
Returned 405 |
| Front Door marked backend unhealthy | ❌ |
| Frontend got 504 Gateway Timeout | 💥 |
| Fix |
@app.api_route("/health", methods=["GET", "HEAD"]) ✅ |
✨ Simple Analogy
Front Door: "Knock knock, are you alive?" (HEAD)
FastAPI: "I only reply to phone calls (GET)" → 405
Front Door: "Okay, unhealthy then."
After the fix:
Front Door: "Knock knock?"
FastAPI: "Yep, alive and ready!" → 200 OK
Front Door: "Perfect, routing traffic now."
💭 Final Thought
This one was a great reminder
sometimes, your system isn’t broken… it’s just misunderstanding the protocol.
A tiny missing method caused a full 504 outage.
But with one line of code, everything started flowing smoothly again. 🚀
Top comments (0)