Go Module 18
HTTP servers and clients with net/http interview questions
Interview questions on HTTP servers and clients with net/http: warm-ups, then what each harder question tests and how a good answer starts.
Warm-up questions
Short questions with short answers, each answered in full.
-
Fresher
What is the difference between the patterns GET / and GET /{$}?
Answered in the lesson HTTP servers with ServeMux method and path patterns
-
Fresher
What does r.WithContext return, and why must a middleware pass that result on?
Answered in the lesson Middleware, request context and ResponseController
-
Fresher
When should an API answer 400, and when 422?
Answered in the lesson JSON APIs: decoding limits, validation and errors
-
Fresher
Why does a Go server need ReadHeaderTimeout when it already has a handler timeout?
Answered in the lesson Production servers: timeouts, shutdown, TLS, CSRF
Questions with model answers
What each question tests, the first step of a good answer and the follow-ups to expect.
-
Mid to senior
You are moving a service from a router where the last matching route won to Go's ServeMux. At start-up it panics: GET /orders/latest/{section} conflicts with GET /orders/{id}/items. Why does ServeMux refuse what the old router accepted, and how do you fix the routes without breaking clients?
- What it tests
- Knowing that ServeMux ranks patterns by strict subsets, reading its conflict message, and changing overlapping routes without breaking the clients that use them.
- First step
- Read the panic: it names a path both patterns match, /orders/latest/items, and a path each matches alone. Find out from the old router's rule and the access logs which handler that path reaches today, and pin it in a routing test before you change any pattern.
- Follow-ups
-
- How would you find every conflicting pair before a deploy rather than at start-up?
- Can existing clients keep their URLs while you change the routes?
- What happens if you also register GET /orders/latest?
From the lesson HTTP servers with ServeMux method and path patterns
Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.Opening the model answers with your pass…
Model answers open with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so the model answers can’t be opened at the moment. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
Mobile apps POST photos to /v1/uploads, and the server registers only POST /v1/uploads/. Built with Go 1.25, some uploads failed with 405; built with Go 1.27, they all work, but every upload costs two round trips. Explain both behaviours and fix the server.
- What it tests
- Knowing the ServeMux trailing-slash redirect, how clients follow a 301 and a 307, and how to register routes so that no client depends on a redirect.
- First step
- Reproduce it in a test: send POST /v1/uploads with a body through the ServeMux and print the status and the Location header. Then let Go's client follow the redirect and log the method and the body length the handler receives.
- Follow-ups
-
- Why did only some apps fail on Go 1.25?
- Would a 308 be better than a 307 here?
- How do you keep new routes from repeating the mistake?
From the lesson HTTP servers with ServeMux method and path patterns
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
An export endpoint streamed progress line by line until someone added an access-log middleware. Now clients get the whole export at the end, and nothing logs an error. Explain why, and fix the middleware so that every handler behind it keeps working.
- What it tests
- Knowing that a ResponseWriter wrapper hides optional interfaces, how http.ResponseController and Unwrap reach them, and how to test streaming through the real chain.
- First step
- Look at how the handler flushes. If it asserts w.(http.Flusher) and skips the flush when the assertion fails, the log's wrapper made it fail silently. Reproduce it with httptest: wrap a recorder in the log middleware and check whether Flush reached the recorder.
- Follow-ups
-
- Why did nothing log an error?
- Should the wrapper implement Flush itself instead of Unwrap?
- How do you keep the next wrapper from breaking streaming again?
From the lesson Middleware, request context and ResponseController
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
An audit finds anonymous DELETE requests that succeeded, although every one of them got 401 Unauthorized. The bearer-token middleware is ten lines long, and its tests pass. Find the likely bug, fix it, and make sure it cannot come back.
- What it tests
- Knowing that http.Error does not end a request, reading a middleware's control flow, and testing side effects rather than status codes alone.
- First step
- Read the branch that writes the 401. If nothing returns after http.Error, next.ServeHTTP still runs. Write a test that sends a DELETE without a token through the middleware and checks that nothing was deleted, not only that the status is 401.
- Follow-ups
-
- Why did the existing tests pass?
- What else should the fixed middleware do besides return?
- How do you list the deletes that were anonymous?
From the lesson Middleware, request context and ResponseController
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Senior to staff
Design the middleware chain of a public JSON API: request IDs, access logs, panic recovery, security headers, bearer-token checks and per-route limits. Give the order from outside in, why each layer sits there, and how you would prove the order in tests.
- What it tests
- Reasoning about what each layer must see, how a chain fails, per-route against global concerns, and tests that pin the chain production runs.
- First step
- Start from what each layer must observe: IDs and logs must see every request, refused ones included; recovery must surround everything that can panic; headers must be set before anything writes. Write that down as a table, then derive the order from it.
- Follow-ups
-
- Where do per-route limits go, such as an upload's body limit or a stream's longer deadline?
- What changes if some routes need no token?
- How do you test that a panic deep inside still gets a request ID, a log line and the headers?
From the lesson Middleware, request context and ResponseController
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A partner's batch job sends POST /orders bodies of 200 MB by mistake, and the service's memory climbs until the process is killed. The handler decodes with json.NewDecoder(r.Body). What do you change, how do you choose the numbers, and what does the partner see afterwards?
- What it tests
- Bounding request bodies with http.MaxBytesReader, sizing a limit from the largest valid body, answering 413 as problem details, and limits that differ by route.
- First step
- Work out the largest valid body and add a margin: for the lesson's orders, 307 bytes against a 64 KB limit. Wrap r.Body with http.MaxBytesReader before decoding, and turn a *http.MaxBytesError into a 413 problem that states the limit.
- Follow-ups
-
- Why not check Content-Length instead?
- The same API also takes 20 MB photo uploads. What changes?
- How much memory can 1,000 requests in flight take after the fix?
From the lesson JSON APIs: decoding limits, validation and errors
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Senior to staff
A validating gateway written in another language refuses orders above 20 copies, then passes them to a Go service that decodes with encoding/json. An audit finds orders for 500 copies; every one of those bodies holds the quantity field twice. Explain how they got through, and fix it.
- What it tests
- Knowing how encoding/json treats duplicate and differently cased names, why two parsers of one request must agree, and how encoding/json/v2 closes the gap.
- First step
- Send {"quantity":2,"quantity":500} to both: the gateway checks one value and the Go service keeps the last, so each checked a different order. Then try {"quantity":2,"QUANTITY":500}, which a case-sensitive gateway reads as two fields and encoding/json as one.
- Follow-ups
-
- Is validating again in the Go service enough?
- How do you move the service to encoding/json/v2 safely?
- What other differences between parsers can do the same?
From the lesson JSON APIs: decoding limits, validation and errors
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Senior to staff
Design the error format of a public orders API used by web and mobile apps in four languages. Which statuses do you use, what does each body contain, how does an app find the field to highlight, and how do you change the format later without breaking old app versions?
- What it tests
- Applying RFC 9457: a status per kind of failure, stable type URIs, errors with pointers, translation, 5xx bodies without internals, and evolving the contract.
- First step
- List the kinds of failure first: wrong media type, too large, not one order, broken rules, server failure. Give each a status and a stable type URI, then decide what an app needs for each: a pointer per field for 422, nothing internal for a 500.
- Follow-ups
-
- Can the titles be translated?
- How do you add a new rule without breaking old app versions?
- What goes into a 500?
From the lesson JSON APIs: decoding limits, validation and errors
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Mid to senior
A Go API's goroutine count climbs to 50,000 overnight while its requests per second stay flat, and the process runs out of memory. It was started with http.ListenAndServe(":8080", mux). What is the likely cause, how do you confirm it, and what do you configure?
- What it tests
- Recognising slow or idle clients that hold connections, the zero Server's missing timeouts, sizing them with Little's law, and confirming the cause with a goroutine profile.
- First step
- Take a goroutine profile and count the goroutines blocked reading requests on server connections. Flat traffic with rising goroutines means connections are being held, not served, and the zero Server has no ReadHeaderTimeout or IdleTimeout to end them.
- Follow-ups
-
- How do you size ReadHeaderTimeout and IdleTimeout?
- Why not set one short ReadTimeout and be done?
- What changes when a load balancer sits in front?
From the lesson Production servers: timeouts, shutdown, TLS, CSRF
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
Every deploy causes a burst of 502 errors at the load balancer for about 5 seconds, although the service calls srv.Shutdown on SIGTERM. Explain the likely causes and design a shutdown sequence that drops nothing.
- What it tests
- The order of a drain: readiness first, a wait for load balancers, Shutdown with a budget, hooks for long-lived connections, all inside the supervisor's grace period.
- First step
- Line up the timeline: when SIGTERM arrives, when the listener closes and when the load balancer stops sending. If the listener closes at once, requests routed to the instance during the load balancer's update are refused, and the load balancer reports them as 502.
- Follow-ups
-
- How long should the wait before Shutdown be?
- What about WebSockets and event streams?
- What if a handler ignores its context?
From the lesson Production servers: timeouts, shutdown, TLS, CSRF
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
A web app in Go uses cookie sessions and HTML forms to move money between a user's accounts. Design its protection against forged requests and stolen session cookies: which Go features you use, which cookie attributes you set, and what is still needed for older browsers and non-browser clients.
- What it tests
- Applying http.CrossOriginProtection, safe methods that never change state, session cookie attributes and prefixes, and authentication for callers that are not browsers.
- First step
- Separate the threats: requests forged by other sites' pages, which CrossOriginProtection refuses from Sec-Fetch-Site or Origin; leaked or stolen cookies, which Secure, HttpOnly, SameSite and the __Host- prefix limit; and non-browser callers, which need authentication.
- Follow-ups
-
- Do you still need CSRF tokens?
- Lax or Strict for the session cookie?
- What must a GET handler never do?
From the lesson Production servers: timeouts, shutdown, TLS, CSRF
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes