Servlet lifecycle, filters, listeners, sessions, MVC, HTTP semantics, caching, REST, CORS/CSRF/XSS, authentication, JWT/OAuth2, async servlets, embedded servers, API design and production web scenarios.
Theory
Q1
Describe the servlet lifecycle.
basic
The container loads the class, creates one instance, calls init(ServletConfig) once, calls service() for every request (dispatching to doGet/doPost and so on), and calls destroy() once on shutdown or undeploy.
Instantiation happens on first request or at startup if load-on-startup is set.
Many request threads share the single instance, so instance fields must be thread-safe.
⚠ Follow-up traps
Is a new servlet created per request? No. One instance, many threads.
Can init be called twice? Not on the same instance; a failed init discards the instance and the container may retry creating another.
#servlet#lifecycle
Q2
Why are servlets not thread-safe by default and what does that imply?
basic
The container runs service() concurrently on a pool of threads against a single servlet instance. Any mutable instance or static field is shared state and needs synchronization or must be avoided.
Keep per-request data in local variables or request attributes.
SingleThreadModel was deprecated and removed (Servlet 6.0) because it never solved shared static/session state.
⚠ Follow-up traps
Is a HttpSession attribute thread-safe? No. Concurrent requests of one user can touch it simultaneously.
Are request/response objects shareable across threads? No, except under defined async handling.
#servlet#thread-safety
Q3
What is the difference between `ServletConfig` and `ServletContext`?
basic
ServletConfig is per servlet (its init params). ServletContext is per web application: shared attributes, context params, resource access, and logging for all servlets.
ServletContext attributes act as application-scoped storage, shared across all users.
One ServletContext per application per JVM (per distributed container node).
⚠ Follow-up traps
Is ServletContext shared across cluster nodes? No, each JVM has its own.
Are init params and context params the same? No, different scopes and declarations.
#servlet#context#config
Q4
What is the difference between `forward`, `include` and `sendRedirect`?
basic
forward and include are server-side hand-offs inside the same request via RequestDispatcher. sendRedirect sends a 302 (or chosen 3xx) to the client, which issues a new request.
Forward: URL in the browser unchanged, same request attributes, single round trip.
Redirect: URL changes, request attributes are lost, extra round trip; use it for Post/Redirect/Get.
Include inserts the target output into the current response.
⚠ Follow-up traps
Can you forward after the response is committed? No, IllegalStateException.
Why Post/Redirect/Get? Prevents duplicate form submission on browser refresh.
#servlet#forward#redirect
Q5
What is a servlet filter and how does the chain work?
basic
A Filter intercepts requests and responses before and after the servlet. doFilter(req, res, chain) runs pre-logic, calls chain.doFilter to continue, then runs post-logic on the way back.
public class TimingFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { long t = System.nanoTime(); try { chain.doFilter(req, res); } finally { System.out.println((System.nanoTime() - t) / 1_000_000 + " ms"); } }}
Use cases: authentication, logging, compression, CORS, encoding.
Not calling chain.doFilter short-circuits the request.
⚠ Follow-up traps
Who decides filter order?web.xml declaration order; with annotations the order is unspecified, so use web.xml or Spring's FilterRegistrationBean.setOrder.
Do filters run on forwards? Only if mapped with the FORWARD dispatcher type.
#filter#chain
Q6
How do filters differ from Spring MVC interceptors?
intermediate
Filters are a servlet-container feature working on raw ServletRequest/ServletResponse before DispatcherServlet. HandlerInterceptor is a Spring MVC feature that runs inside DispatcherServlet and knows the resolved handler.
Filters can wrap the request/response (body caching, compression); interceptors cannot replace them.
Exceptions thrown in filters bypass @ControllerAdvice.
⚠ Follow-up traps
Where would you put CORS or request body logging? A filter, since it must wrap the raw streams and apply to non-MVC paths.
Does postHandle run if the handler throws? No, but afterCompletion does.
#filter#interceptor#spring-mvc
Q7
What listeners does the servlet API provide?
basic
Listeners observe lifecycle and attribute events: ServletContextListener (app start/stop), HttpSessionListener (session created/destroyed), ServletRequestListener, and attribute listeners for context, session and request.
Typical use: initialize connection pools or caches in contextInitialized, release them in contextDestroyed.
HttpSessionBindingListener and HttpSessionActivationListener are implemented by the attribute values themselves.
⚠ Follow-up traps
When does sessionDestroyed fire? On invalidate() or timeout expiry, not when the browser closes.
Is a ServletContextListener called before servlets init? Yes, contextInitialized runs before filters and servlets are initialized.
#listener#lifecycle
Q8
How do HTTP sessions work?
basic
The server creates an HttpSession holding state in memory keyed by a random ID, and sends the ID to the client in a cookie (JSESSIONID). The client returns it on each request, and the server looks the session up.
Created lazily on request.getSession() (or getSession(true)); getSession(false) returns null if none exists.
Idle timeout via session-timeout or setMaxInactiveInterval.
URL rewriting (;jsessionid=) is the cookie-less fallback and should be disabled in modern apps.
⚠ Follow-up traps
Does the session die when the browser closes? The cookie is session-scoped and dropped, but the server session lives until timeout.
Does getSession(false) create a session? No.
#session#cookie
Q9
What are the important cookie attributes?
basic
HttpOnly blocks JavaScript access, Secure restricts to HTTPS, SameSite (Strict, Lax, None) limits cross-site sending, and Domain, Path, Max-Age/Expires control scope and lifetime.
SameSite=None requires Secure.
Browsers default to Lax when unspecified (Chrome and others).
Session cookies should be HttpOnly; Secure; SameSite=Lax.
⚠ Follow-up traps
Does HttpOnly stop XSS? No, it only prevents cookie theft by script; XSS can still make requests as the user.
Is a cookie sent to subdomains? Only when Domain is set explicitly (or by the host-only default rules, which exclude subdomains).
#cookie#security
Q10
What is session fixation and how do you prevent it?
intermediate
An attacker plants a known session ID in the victim's browser, the victim logs in, and the unchanged ID now identifies an authenticated session the attacker can reuse. Prevent by issuing a new session ID at login.
Servlet 3.1+: request.changeSessionId(); Spring Security does this by default (changeSessionId strategy).
Also disable URL-based session IDs and set cookie flags.
⚠ Follow-up traps
Is invalidate + new session equivalent? It works but loses attributes you wanted to keep; changeSessionId preserves them.
Should the ID change on logout? Invalidate the session on logout.
#session#security#fixation
Q11
What are the options for session management in a clustered deployment?
intermediate
Sticky sessions, session replication, an external session store (Redis, JDBC via Spring Session), or stateless tokens.
Sticky sessions: simplest, but node failure loses sessions and load balances unevenly.
Replication: heavy network traffic and memory; poor scaling.
External store: survives restarts, scales; adds a network hop and serialization cost.
Stateless: no server state, but revocation is harder.
⚠ Follow-up traps
What must session attributes implement for replication?Serializable.
Does Spring Session change application code? Mostly no; it replaces HttpSession implementation via a filter.
#session#cluster#scalability
Q12
What is JSP and how does it relate to servlets?
basic
JSP is an HTML template with embedded Java/EL/tag libraries. The container translates each JSP into a servlet class, compiles it on first access, and executes it like any servlet.
Why is the first JSP request slow? It triggers translation and compilation.
Is JSP the default view tech in Spring Boot? No. It has limited support with embedded containers (WAR packaging preferred); Thymeleaf or templates are typical.
#jsp#view
Q13
Explain the MVC pattern in Java web applications.
basic
Model holds data and business logic, View renders output, Controller handles input and chooses the view. It separates concerns so each part can change independently.
Servlet-era: servlet as controller, JSP as view, POJOs as model.
Spring MVC: DispatcherServlet is the front controller.
⚠ Follow-up traps
Is MVC used for REST APIs? The view becomes serialization (JSON); controller and model remain.
Where does business logic belong? In the service layer, not controllers.
#mvc#architecture
Q14
Describe the request flow in Spring MVC.
intermediate
DispatcherServlet receives the request, HandlerMapping finds the handler, interceptors run preHandle, HandlerAdapter invokes the controller with resolved arguments, the return value is processed by a HttpMessageConverter (REST) or ViewResolver (views), and exceptions go to HandlerExceptionResolver.
Where is JSON conversion done?HttpMessageConverter (Jackson) selected by Accept/Content-Type.
Are filters before or after DispatcherServlet? Before.
#spring-mvc#dispatcherservlet
Q15
Which HTTP methods exist and what are safe and idempotent methods?
basic
Safe methods do not change server state (GET, HEAD, OPTIONS, TRACE). Idempotent methods yield the same server state when repeated (those plus PUT and DELETE). POST and PATCH are neither guaranteed.
Idempotency is about server state, not identical responses: a repeated DELETE may return 404 the second time.
Clients, proxies and browsers rely on these properties to retry and prefetch.
⚠ Follow-up traps
Is PUT idempotent? Yes, it replaces the resource with the same representation.
Is PATCH idempotent? Not necessarily (increment by 1 is not).
#http#methods#idempotency
Q16
When do you use PUT, PATCH and POST?
intermediate
POST creates a subordinate resource or triggers processing, with a server-assigned URI. PUT fully replaces a resource at a client-known URI. PATCH applies a partial modification.
PATCH formats: JSON Merge Patch (application/merge-patch+json) or JSON Patch (application/json-patch+json).
PUT creates the resource if it does not exist (201) or replaces it (200/204).
⚠ Follow-up traps
What does PUT do to omitted fields? They are reset, since it is a full replacement.
How do you make POST safe to retry? Use an Idempotency-Key header and dedupe on the server.
#http#put#patch#post
Q17
Summarize the HTTP status code classes and commonly misused codes.
401 or 403 for a missing token? 401 (and WWW-Authenticate); 403 means authenticated but not allowed.
Is returning 200 with an error body acceptable? No, it breaks clients, caches and monitoring.
#http#status-codes
Q18
What is the difference between 301, 302, 307 and 308?
intermediate
301 and 308 are permanent; 302 and 307 are temporary. 307 and 308 require the client to keep the method and body, while historically 301/302 allowed browsers to turn POST into GET.
Use 303 See Other after a POST to redirect to a GET resource.
Permanent redirects are cached aggressively by browsers.
⚠ Follow-up traps
Which code for Post/Redirect/Get? 303 (302 works in practice).
Can a wrong 301 be undone easily? No, clients cache it.
#http#redirect
Q19
Which important HTTP headers should a backend engineer know?
Content-Type describes the request body; Accept describes desired response types.
Behind a proxy, use X-Forwarded-* (with server.forward-headers-strategy in Spring Boot) to recover client IP, scheme and host.
⚠ Follow-up traps
Can X-Forwarded-For be trusted? Only from known proxies; clients can spoof it.
Are header names case-sensitive? No.
#http#headers
Q20
How does HTTP caching work with `Cache-Control`?
intermediate
Cache-Control directives tell browsers and shared caches how long and whether to store a response: max-age, s-maxage, no-cache, no-store, public, private, must-revalidate, immutable.
no-cache means store but revalidate before use; no-store means never store.
private forbids shared caches (CDNs); use it for user-specific data.
Does no-cache disable caching? No, it forces revalidation; no-store disables storage.
What is Vary for? It tells caches which request headers (e.g. Accept-Encoding) alter the response.
#http#caching#cache-control
Q21
How do ETag and conditional requests work?
intermediate
The server returns an ETag validator. The client sends it back as If-None-Match; if unchanged the server replies 304 Not Modified with no body, saving bandwidth.
Strong ETags mean byte-identical; weak ones (W/"...") mean semantically equivalent.
Last-Modified/If-Modified-Since is the date-based alternative.
If-Match on PUT/DELETE gives optimistic concurrency (412 Precondition Failed on mismatch).
@GetMapping("/items/{id}")ResponseEntity<Item> get(@PathVariable long id) { Item i = service.find(id); return ResponseEntity.ok().eTag("\"" + i.version() + "\"").body(i);}
⚠ Follow-up traps
Does ETag save server work? Not necessarily; it saves bandwidth unless you compute the tag cheaply (e.g. from a version column).
Which status for failed If-Match? 412.
#etag#caching#conditional
Q22
What are the REST architectural constraints?
basic
Client-server, stateless, cacheable, uniform interface, layered system, and optional code-on-demand. The uniform interface covers resource identification by URI, manipulation through representations, self-descriptive messages, and HATEOAS.
Stateless: each request carries all context; the server holds no client session state.
Resources are nouns; verbs come from HTTP methods.
⚠ Follow-up traps
Is a JWT-authenticated API stateless? Yes in the REST sense, since the token travels with each request.
Is every JSON-over-HTTP API RESTful? No; many are RPC-style.
#rest#principles
Q23
What is the Richardson Maturity Model?
intermediate
It grades APIs on four levels: 0 single endpoint tunneling (RPC/POX), 1 resources with individual URIs, 2 proper HTTP verbs and status codes, 3 hypermedia controls (HATEOAS).
Most production APIs stop at level 2.
Level 3 embeds links (_links) so clients discover actions; Spring HATEOAS supports it.
⚠ Follow-up traps
Is level 2 "REST"? Strictly per Fielding, only level 3 qualifies.
What does HATEOAS buy you? Decoupling clients from URI structure and workflow state.
#rest#maturity#hateoas
Q24
How should REST URIs be designed?
basic
Use plural nouns for collections, hierarchy for ownership, and HTTP methods for actions: GET /orders, GET /orders/42, GET /orders/42/items.
Lowercase, hyphenated, no verbs, no file extensions.
Filtering, sorting and paging go in query parameters.
For non-CRUD actions, model a sub-resource (POST /orders/42/cancellation) rather than /cancelOrder.
⚠ Follow-up traps
Deep nesting like /a/1/b/2/c/3? Avoid beyond two levels; expose the child by its own ID.
Should IDs be sequential integers? Prefer non-guessable IDs where enumeration is a risk.
#rest#uri-design
Q25
What is content negotiation?
intermediate
The client states preferences with Accept (and Accept-Language, Accept-Encoding) using quality values, and the server picks a representation, returning 406 Not Acceptable if none matches. The request body type is declared with Content-Type, and unsupported ones yield 415.
Spring MVC: produces/consumes on mappings select handlers by media type.
Always send Vary: Accept when the representation depends on it.
406 versus 415? 406 is about the response type, 415 about the request body type.
What does q=0.8 mean? Relative preference weight between 0 and 1.
#content-negotiation#http
Q26
What is CORS and how does it work?
intermediate
CORS lets a server tell browsers which cross-origin pages may read its responses. For non-simple requests the browser sends a preflight OPTIONS with Origin, Access-Control-Request-Method and -Headers, and the server answers with Access-Control-Allow-* headers.
Simple requests (GET/POST with basic headers and content types) skip the preflight.
Credentials require an explicit origin (not *) and Access-Control-Allow-Credentials: true.
Preflight results are cached via Access-Control-Max-Age.
⚠ Follow-up traps
Does CORS protect the server? No, it restricts browsers only; curl ignores it.
Can you combine * origin with credentials? No, the browser rejects it.
#cors#security
Q27
What is CSRF and how do you defend against it?
intermediate
Cross-site request forgery makes a victim's browser send an authenticated, state-changing request because cookies are attached automatically. Defend with synchronizer or double-submit CSRF tokens, SameSite cookies, and checking Origin/Referer.
Spring Security enables CSRF protection by default for browser flows.
APIs authenticated with an Authorization header (not cookies) are not CSRF-prone, so it is commonly disabled for stateless APIs.
⚠ Follow-up traps
Does CORS prevent CSRF? No; simple cross-site form posts are sent regardless.
Do safe methods need CSRF tokens? No, provided GET never changes state.
#csrf#security
Q28
What is XSS and how do you prevent it?
intermediate
Cross-site scripting injects attacker script into pages viewed by others. Types: stored, reflected, DOM-based. Prevent with context-aware output encoding, input validation, a Content Security Policy, and HttpOnly cookies.
Template engines (Thymeleaf th:text, JSTL <c:out>) escape by default; th:utext and raw ${} in JSP scriptlets do not.
JSON APIs must send Content-Type: application/json plus X-Content-Type-Options: nosniff.
⚠ Follow-up traps
Is input sanitization enough? No, encode on output for the specific context (HTML, attribute, JS, URL).
Does a CSP replace encoding? No, it is defense in depth.
#xss#security
Q29
What other security headers matter for web apps?
intermediate
Strict-Transport-Security (force HTTPS), Content-Security-Policy, X-Content-Type-Options: nosniff, X-Frame-Options or CSP frame-ancestors (clickjacking), Referrer-Policy, and Permissions-Policy.
Spring Security adds several of these by default.
HSTS is only honoured over HTTPS responses.
⚠ Follow-up traps
Why not set HSTS with a huge max-age first? A misconfiguration locks users out of HTTP subdomains for that long.
What does nosniff stop? Browsers guessing a script type from content.
#security#headers
Q30
What is the difference between authentication and authorization?
basic
Authentication verifies who you are (credentials, token). Authorization decides what that identity may do (roles, permissions, policies).
Failures: unauthenticated gives 401; authenticated but denied gives 403.
Authorization models: RBAC, ABAC, ownership checks on each object.
⚠ Follow-up traps
Is a role check at the URL level enough? No; object-level checks prevent IDOR (insecure direct object reference).
Where do you enforce it? On the server, never only in the UI.
#authentication#authorization
Q31
How should passwords be stored?
basic
Hash with a slow, salted, adaptive algorithm: bcrypt, scrypt, Argon2 or PBKDF2. Never encrypt reversibly and never use plain SHA-256/MD5.
Spring Security: PasswordEncoder (BCryptPasswordEncoder, DelegatingPasswordEncoder with {bcrypt} prefixes enabling migration).
Salt is stored with the hash; a pepper is optional and kept outside the database.
⚠ Follow-up traps
Why not SHA-256 with salt? It is fast, so brute forcing is cheap.
How do you raise the cost later? Re-hash on next successful login.
#authentication#passwords
Q32
What is HTTP Basic, form login and token-based authentication?
basic
Basic sends base64 user:password in Authorization every request (needs TLS). Form login posts credentials once and then relies on a session cookie. Token auth sends a bearer token (Authorization: Bearer ...) on each request.
Base64 is encoding, not encryption.
Tokens suit APIs and mobile; sessions suit server-rendered apps.
⚠ Follow-up traps
Is Basic over HTTPS acceptable? For internal or machine clients, yes; it still sends the password each time.
Where should a SPA store tokens? In-memory or an HttpOnly cookie; localStorage is exposed to XSS.
#authentication#basic#token
Q33
What is a JWT and how is it structured?
intermediate
A JSON Web Token is header.payload.signature, each Base64URL-encoded. The signature (HS256 shared secret, RS256/ES256 key pair) makes it tamper-evident; the payload is readable by anyone.
Standard claims: iss, sub, aud, exp, nbf, iat, jti.
Validate signature, algorithm whitelist, exp, iss and aud.
⚠ Follow-up traps
Is a JWT encrypted? Not by default; that needs JWE.
What is the alg: none attack? A forged unsigned token accepted by libraries that trust the header; always pin accepted algorithms.
#jwt#token
Q34
What are the drawbacks of JWTs and how do you handle revocation?
advanced
JWTs are self-contained so the server cannot invalidate one before exp without extra state. Mitigate with short-lived access tokens plus refresh tokens, a denylist keyed by jti, or key rotation.
Rotate refresh tokens and detect reuse to spot theft.
Large claims inflate every request's headers.
⚠ Follow-up traps
Does logout invalidate a JWT? Not unless you track it server-side.
HS256 or RS256 for multiple services? RS256/ES256 so verifiers hold only the public key.
#jwt#revocation
Q35
Explain the OAuth2 roles and the authorization code flow.
intermediate
Roles: resource owner, client, authorization server, resource server. In the authorization code flow the client redirects the user to the authorization server, receives a one-time code at its redirect URI, and exchanges it (server-side, with client credentials or PKCE) for access and refresh tokens.
Use PKCE for public clients (SPAs, mobile); it is recommended for all clients in OAuth 2.1.
state parameter protects against CSRF on the callback.
⚠ Follow-up traps
Why not return tokens directly in the redirect (implicit flow)? Tokens leak via URLs and history; implicit is deprecated.
Is OAuth2 authentication? No, it is delegated authorization; OIDC adds identity via the ID token.
#oauth2#authorization-code
Q36
What are the other OAuth2 grant types and where are they used?
intermediate
Client credentials (service-to-service, no user), refresh token (renew access), device code (input-limited devices), and the deprecated password and implicit grants.
Client credentials: the client is the resource owner; scope by service identity.
Resource servers validate tokens by JWT signature or by introspection (RFC 7662).
⚠ Follow-up traps
Why is the password grant discouraged? The client handles user credentials, defeating delegation.
JWT validation versus introspection? JWT is offline and fast but not instantly revocable; introspection is online and revocable.
#oauth2#grants
Q37
How does file upload work with servlets and Spring?
intermediate
Browsers send multipart/form-data. Servlet 3.0+ exposes parts via request.getParts() (@MultipartConfig); Spring wraps them as MultipartFile.
Spring Boot limits: spring.servlet.multipart.max-file-size (default 1MB) and max-request-size (10MB).
Small parts stay in memory; larger spill to temp files (file-size-threshold).
⚠ Follow-up traps
Is getOriginalFilename() safe as a path? No, it enables path traversal; generate your own name.
Is Content-Type of the part trustworthy? No, the client controls it; verify content (magic bytes).
#file-upload#multipart
Q38
What are the security considerations for file uploads?
advanced
Enforce size limits, allowlist types by content inspection, rename files, store outside the web root (or in object storage), scan for malware, and serve downloads with a safe Content-Type and Content-Disposition: attachment.
Beware zip bombs and decompression, image parser vulnerabilities, and SVG scripts.
Do not execute or serve uploaded files from a path that runs JSP/scripts.
⚠ Follow-up traps
Why store in S3 with pre-signed URLs? Large uploads bypass your app threads and bandwidth.
Does an extension check suffice? No, evil.jpg.jsp or mismatched content defeats it.
#file-upload#security
Q39
How do async servlets work?
advanced
request.startAsync() returns an AsyncContext and releases the container thread while the work continues elsewhere. When finished, call asyncContext.complete() or dispatch() to write the response.
Every filter in the chain must also declare asyncSupported = true.
Register an AsyncListener for timeouts and errors.
⚠ Follow-up traps
Does async make the work itself faster? No, it frees the request thread to serve others during waits.
What happens on timeout without a listener? The container responds with an error (typically 500).
#async#servlet
Q40
How does Spring MVC support async handling?
intermediate
Return Callable, DeferredResult, CompletableFuture, StreamingResponseBody or SseEmitter from a controller. Spring starts async processing, frees the container thread, and re-dispatches when the result is ready.
Callable runs on the MVC async executor; configure a bounded AsyncTaskExecutor (the default simple executor creates a thread per task).
DeferredResult is completed from any thread, e.g. a message listener.
⚠ Follow-up traps
Is a Callable return reactive? No, it still occupies another thread.
Do Spring Security context and MDC propagate? Security context is propagated by Spring; MDC needs a task decorator.
#async#spring-mvc#deferredresult
Q41
Compare embedded Tomcat, Jetty and Undertow.
intermediate
All are servlet containers that run inside the application JAR. Tomcat is the Spring Boot default and the most widely deployed; Jetty is lightweight and strong for async/WebSocket and embedding; Undertow is non-blocking-core with low memory and high throughput.
Switch by excluding spring-boot-starter-tomcat and adding spring-boot-starter-jetty or -undertow.
Spring Boot 3 requires Servlet 6 (Jakarta namespace); Undertow lacks Servlet 6.1 support in Boot 3.x/4 so check compatibility.
⚠ Follow-up traps
Why fat JAR with embedded server? Single deployable unit, per-app tuning, and container-friendly.
What is Tomcat's default thread pool max? 200.
#embedded#tomcat#jetty#undertow
Q42
What is the Tomcat connector model (NIO, acceptor, poller, workers)?
advanced
An acceptor thread accepts sockets, poller threads watch idle keep-alive connections via NIO, and a worker thread from the executor pool handles each active request until the response completes.
max-connections (default 8192) bounds open sockets; accept-count (default 100) is the OS backlog queue.
Blocking servlet I/O still ties up a worker for the whole request.
⚠ Follow-up traps
Are 8192 connections served concurrently? No; only threads.max requests execute at once, the rest wait idle or queue.
Is NIO the same as async handling? No, NIO is the connector; the servlet code still blocks the worker.
#tomcat#nio#threads
Q43
Compare thread-per-request and reactive models.
intermediate
Thread-per-request dedicates a thread to each request, blocking on I/O; it is simple but capacity is bounded by thread count. Reactive (WebFlux/Netty) uses a few event-loop threads with non-blocking I/O and backpressure, scaling to many concurrent slow connections.
Reactive requires non-blocking drivers end to end (R2DBC, reactive HTTP clients); one blocking call stalls an event loop.
Debugging and stack traces are harder; CPU-bound work gains nothing.
Java 21 virtual threads (spring.threads.virtual.enabled=true) give thread-per-request code with high concurrency.
⚠ Follow-up traps
Is WebFlux faster for a CPU-bound API? No.
Do virtual threads make blocking JDBC free? They free the carrier thread, but the connection pool still limits concurrency.
#reactive#thread-per-request#webflux
Q44
How do virtual threads change Java web servers?
advanced
Java 21 virtual threads let servers run each request on a cheap JVM-managed thread, so blocking calls no longer waste OS threads. Tomcat/Jetty run requests on virtual threads when configured.
Spring Boot 3.2+: spring.threads.virtual.enabled=true.
Pinning: blocking inside synchronized pinned the carrier in Java 21 (largely fixed in Java 24); prefer ReentrantLock.
Bound downstream resources (pools, semaphores) because thread count is no longer the throttle.
⚠ Follow-up traps
Does it speed up CPU-bound code? No.
Should you pool virtual threads? No, create one per task.
#virtual-threads#java21
Q45
How should REST APIs be versioned?
intermediate
Common strategies: URI path (/v1/orders), header (X-API-Version), media type (application/vnd.acme.v2+json), and query parameter. Path versioning is the most visible and cache friendly; media type is purist.
Prefer additive, backward-compatible changes; version only on breaking changes.
Publish deprecation with Deprecation/Sunset headers and a timeline.
Spring Framework 7 adds built-in API versioning support in MVC mappings.
⚠ Follow-up traps
Is adding a JSON field breaking? Not for tolerant readers; removing or renaming one is.
How many versions to support? As few as possible, with a published sunset policy.
#api-versioning#rest
Q46
Compare offset, page-number and cursor pagination.
intermediate
Offset/page uses LIMIT n OFFSET m, which is simple but degrades on deep pages and shows duplicates or gaps when data changes. Cursor (keyset) pagination uses the last seen sort key, giving stable, constant-cost pages.
SELECT id, created_at FROM ordersWHERE (created_at, id) < (:lastCreated, :lastId)ORDER BY created_at DESC, id DESCLIMIT 20;
Return nextCursor (opaque, encoded) and optionally Link headers.
Cursors cannot jump to an arbitrary page.
⚠ Follow-up traps
Why include id in the sort? A unique tiebreaker; otherwise equal timestamps skip or repeat rows.
Does OFFSET 100000 cost O(1)? No, the database scans and discards those rows.
#pagination#api-design
Q47
How does Spring Data support pagination in controllers?
basic
Accept a Pageable argument (?page=0&size=20&sort=name,asc) and return Page<T>, which carries content plus total counts. Slice<T> skips the count query.
Page runs an extra COUNT(*), which is costly on large tables.
Cap spring.data.web.pageable.max-page-size (default 2000) to prevent abuse.
⚠ Follow-up traps
Page index base? Zero-based by default.
When use Slice? Infinite scroll where total is unnecessary.
#pagination#spring-data
Q48
How is rate limiting implemented and what algorithms exist?
intermediate
Common algorithms: fixed window, sliding window, token bucket and leaky bucket. Token bucket allows controlled bursts and is the most common. Enforce at the gateway or with a shared store (Redis) keyed by user, API key or IP.
Respond 429 Too Many Requests with Retry-After and RateLimit-* headers.
Libraries: Bucket4j, Resilience4j RateLimiter, API gateway plugins.
Per-node in-memory limits multiply by node count in a cluster.
⚠ Follow-up traps
Fixed window flaw? Bursts at window boundaries allow up to 2x the limit.
Rate limiting versus load shedding? The first enforces per-client fairness; the second protects the server overall.
#rate-limiting#api-design
Q49
What is OpenAPI and how is it used with Java?
basic
OpenAPI is a language-neutral specification (YAML/JSON) describing endpoints, schemas, auth and responses. Swagger UI renders it, and tools generate clients and servers from it.
Code-first: springdoc-openapi scans Spring annotations and serves /v3/api-docs and /swagger-ui.html.
Design-first: write the spec, generate stubs with openapi-generator, and validate contract compliance.
Annotations like @Operation, @Schema, @ApiResponse enrich docs.
⚠ Follow-up traps
Is Swagger the same as OpenAPI? Swagger is the tooling; OpenAPI is the spec (formerly the Swagger spec).
Should docs UI be public in production? Usually restricted or disabled.
#openapi#swagger#documentation
Q50
How should errors be represented in REST APIs?
intermediate
Return an appropriate status code and a consistent body. RFC 9457 (formerly 7807) Problem Details defines application/problem+json with type, title, status, detail, instance.
Spring 6 / Boot 3: ProblemDetail and ErrorResponse, enabled via spring.mvc.problemdetails.enabled=true.
Centralize with @RestControllerAdvice and @ExceptionHandler.
Never leak stack traces or SQL in responses.
⚠ Follow-up traps
400 versus 422? 400 for malformed syntax, 422 for well-formed but semantically invalid.
Do exceptions in filters reach @ControllerAdvice? No.
#error-handling#problem-details
Q51
What are HTTP/1.1 keep-alive, HTTP/2 and HTTP/3 differences?
intermediate
HTTP/1.1 reuses connections (keep-alive) but handles one request at a time per connection (head-of-line blocking). HTTP/2 multiplexes streams over one TCP connection with header compression (HPACK). HTTP/3 runs over QUIC (UDP) removing TCP-level head-of-line blocking.
Browsers require TLS for HTTP/2 in practice; Spring Boot enables it with server.http2.enabled=true.
HTTP/2 server push is deprecated and removed from major browsers.
⚠ Follow-up traps
Does HTTP/2 remove the need for connection pools between services? Fewer connections are needed, but not none.
Does HTTP/2 fix TCP head-of-line blocking? No, only HTTP/3 does.
#http2#http3#performance
Q52
What is the difference between Transfer-Encoding chunked, Content-Length and streaming responses?
advanced
Content-Length declares the body size up front. Transfer-Encoding: chunked (HTTP/1.1) sends the body in sized chunks when the length is unknown, enabling streaming. HTTP/2 uses frames instead.
Use StreamingResponseBody, ResponseBodyEmitter or SseEmitter in Spring MVC for large or incremental output.
Server-Sent Events use text/event-stream over a long-lived response.
⚠ Follow-up traps
Can a response have both headers? No, they are mutually exclusive.
Why does buffering break SSE? Proxies or compression that buffer delay events; disable proxy buffering.
#http#streaming#chunked
Scenarios
Q53
A user logs in and an attacker who set the session cookie earlier now has access. What is wrong and how do you fix it?
intermediate
This is session fixation: the session ID did not change on authentication. Rotate the ID at login.
Spring Security does this by default; custom login code often misses it.
Also set HttpOnly, Secure, SameSite, and disable URL rewriting (<tracking-mode>COOKIE</tracking-mode>).
⚠ Follow-up traps
Would HTTPS alone fix it? No, the flaw is in ID lifecycle, not transport.
Should you rotate on privilege change too? Yes, on any elevation.
#session-fixation#security
Q54
After a deployment, all users are logged out. Why, and how can you avoid it?
intermediate
Sessions live in the JVM heap of the old process, so a restart drops them. Persist sessions externally (Spring Session with Redis/JDBC) or use stateless tokens.
Tomcat can persist sessions across restarts with PersistentManager/StandardManager serialization, only when attributes are Serializable and the restart is graceful.
Rolling deployments with sticky sessions still lose sessions of drained nodes.
⚠ Follow-up traps
Will an external store need serialization? Yes, use Serializable or a JSON serializer, and be careful with class versioning across releases.
Does a bigger timeout help? No, the data is gone with the process.
#session#deployment
Q55
The app throws `NotSerializableException` only in production. What happened?
intermediate
Production uses a clustered or persisted session store that serializes attributes, while dev keeps them in memory. A non-Serializable object (or a field in its graph) was stored in the session.
Fix: implement Serializable with a serialVersionUID, store only small ID/DTO values, and mark heavy fields transient.
Never put entities, connections or streams in the session.
⚠ Follow-up traps
Why storing entities in sessions is poor? Stale data, lazy loading outside a transaction, and large replication payloads.
Can the problem hide until failover? Yes, if replication is async or only triggered on node loss.
#session#serialization
Q56
Requests hang under load and Tomcat threads are all busy. How do you diagnose it?
advanced
Take several thread dumps (jcmd <pid> Thread.print or jstack) a few seconds apart and look at what the http-nio-*-exec-* threads are doing; consistent stacks in the same frame show the bottleneck.
Common causes: slow DB queries, exhausted connection pool (threads parked in HikariPool.getConnection), a downstream HTTP call without a timeout, lock contention, or deadlock.
Check metrics: tomcat.threads.busy, pool active/pending, and request latency percentiles.
Remedies: timeouts everywhere, bulkheads, bound pools, and fail fast.
⚠ Follow-up traps
Will raising threads.max fix it? Usually it moves the bottleneck and can overwhelm the database.
How is deadlock shown?jstack prints "Found one Java-level deadlock".
#stuck-requests#thread-dump#tomcat
Q57
Threads are waiting in `HikariPool.getConnection` and users see 500 errors after 30 seconds. What does that mean?
intermediate
The connection pool is exhausted: all connections are borrowed and requests wait up to connectionTimeout (default 30s) before throwing SQLTransientConnectionException. Look for leaked connections, long transactions, or work holding a connection while calling slow external services.
Enable leakDetectionThreshold to log borrowers.
Keep transactions short; avoid remote calls inside them; Open-Session-in-View holds connections for the whole request, so consider spring.jpa.open-in-view=false.
Pool sizing is small (roughly cores x 2 + spindles); more is rarely better.
⚠ Follow-up traps
Should you just raise maximumPoolSize? Only if the database can handle it; otherwise you shift the queue into the DB.
Why is 30s timeout harmful? Requests pile up; lower it to fail fast.
#connection-pool#stuck-requests
Q58
A client calls your endpoint and it times out, but the server log shows the work completed. What happens when the client retries a POST?
intermediate
The retry executes the operation again, creating duplicates (double charge, duplicate order). Make it idempotent with an Idempotency-Key header: store key and result, return the stored response on replays.
Enforce with a unique DB constraint on the key, not a check-then-insert.
Reject the same key with a different payload (422/409).
⚠ Follow-up traps
Is retrying a PUT safe? Yes, by definition of idempotency.
How long keep keys? Long enough to cover client retry windows, typically 24 hours.
#idempotency#retries
Q59
A client uploads a 2 GB file and the server runs out of memory. What went wrong?
advanced
The upload was buffered in memory (e.g. @RequestBody byte[], file.getBytes(), or an in-memory multipart parser). Stream it to disk or storage instead and enforce limits.
Set max-file-size, max-request-size, and a proxy limit (client_max_body_size).
Better: direct-to-object-store uploads via pre-signed URLs, or resumable/chunked uploads.
⚠ Follow-up traps
Does Spring multipart spill to disk automatically? Past file-size-threshold yes, but getBytes() re-loads it into heap.
Which status for oversized bodies? 413 Content Too Large.
#large-payload#upload#memory
Q60
A JSON endpoint receives a 500 MB body or deeply nested JSON. How do you defend it?
advanced
Limit body size at the proxy and container (server.tomcat.max-swallow-size, max-http-form-post-size, gateway limits), stream-parse where possible, and apply Jackson StreamReadConstraints (max nesting depth, string length, number length).
Jackson 2.15+ enforces default limits (nesting 1000, string 20 MB; later raised/configurable).
Validate early with Bean Validation and reject unknown fields if appropriate.
Add timeouts for slow body delivery (slowloris).
⚠ Follow-up traps
Does Content-Length check suffice? Chunked uploads have no declared length, so enforce a byte counter.
Why limit nesting? Deep recursion causes StackOverflowError or CPU burn.
#large-payload#dos#json
Q61
The browser console shows "blocked by CORS policy" but Postman works. Why?
basic
Postman ignores CORS; browsers enforce it. The server response lacks Access-Control-Allow-Origin for the page's origin, or the preflight failed.
@BeanCorsConfigurationSource cors() { CorsConfiguration c = new CorsConfiguration(); c.setAllowedOrigins(List.of("https://app.example.com")); c.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE")); c.setAllowedHeaders(List.of("Authorization", "Content-Type")); UrlBasedCorsConfigurationSource s = new UrlBasedCorsConfigurationSource(); s.registerCorsConfiguration("/**", c); return s;}
With Spring Security, enable http.cors(Customizer.withDefaults()) so the CORS filter runs before security rejects the unauthenticated preflight.
⚠ Follow-up traps
Why does the preflight return 401? The OPTIONS request has no credentials and security blocked it before CORS handling.
Is * fine for production? Not with credentials, and rarely wise for private APIs.
#cors#debugging
Q62
A fetch with cookies to your API fails CORS even though `Allow-Origin` is set. What is missing?
intermediate
Credentialed requests need Access-Control-Allow-Credentials: true, a specific (non-wildcard) Allow-Origin, and the client must set credentials: 'include'. Wildcards for headers and methods are also not honoured in credentialed mode.
SameSite=None; Secure is needed on the cookie for cross-site contexts.
Add Vary: Origin when the allowed origin is echoed dynamically.
⚠ Follow-up traps
Why Vary: Origin? Otherwise a cache may serve one origin's CORS headers to another.
Are same-site, cross-origin calls affected by SameSite? No, SameSite concerns site, not origin.
#cors#credentials
Q63
Your POST endpoint works in the browser from another site and changes state using the user's cookie. How could that be abused and how do you stop it?
intermediate
An attacker page auto-submits a hidden form to your endpoint; the browser attaches the session cookie, so the action executes. Add CSRF tokens (Spring Security default), set SameSite=Lax/Strict, and verify Origin.
If rich text is required, sanitize with an allowlist library (OWASP Java HTML Sanitizer).
Add a CSP and HttpOnly cookies as backup.
⚠ Follow-up traps
Is escaping on input (when saving) correct? No; encoding depends on output context.
Why can JSP ${param.x} be dangerous? EL does not escape unless <c:out> or fn:escapeXml is used.
#xss#scenario
Q65
A user changes `/orders/1001` to `/orders/1002` and sees another user's order. What is the flaw?
intermediate
Broken object-level authorization (IDOR). The endpoint authenticates but does not verify ownership. Check that the resource belongs to the caller on every access.
@PreAuthorize("@orderAccess.canView(#id, authentication)")@GetMapping("/orders/{id}")Order get(@PathVariable long id) { return service.find(id); }
Alternative: scope queries by owner (findByIdAndUserId) and return 404 to avoid revealing existence.
Random IDs help but are not a substitute.
⚠ Follow-up traps
Is a role check (ROLE_USER) enough? No, it is not object specific.
403 or 404 for foreign resources? 404 hides existence; 403 is more explicit.
#authorization#idor
Q66
Your API returns 403 for an expired token. What should it return and why does it matter?
basic
401 Unauthorized with WWW-Authenticate: Bearer error="invalid_token". The client reacts to 401 by refreshing the token or re-authenticating, whereas 403 signals that nothing will help.
Spring Security's BearerTokenAuthenticationEntryPoint sets this correctly for resource servers.
403 is for authenticated users lacking permission.
⚠ Follow-up traps
Is 401 a misnomer? Yes, it means unauthenticated, not unauthorized.
Should the response explain exactly why the token failed? Keep detail minimal to avoid helping attackers.
#authentication#status-codes
Q67
A JWT contains `"role":"admin"` and the client edited the payload. Why does the server still accept it, or does it?
advanced
If the server verifies the signature, the edit invalidates it and the token is rejected. It is accepted only if verification is missing, the secret is weak or leaked, or alg: none/algorithm confusion is allowed.
Algorithm confusion: an RS256 public key used as an HS256 secret by a naive library. Pin the expected algorithm and key.
Never trust decoded claims without verifying the signature (decoding is not validating).
⚠ Follow-up traps
Can the client read the payload? Yes, never put secrets in claims.
Is HS256 safe with a short secret? No, brute forceable; use at least 256-bit random keys.
#jwt#security#scenario
Q68
Where should a SPA store the access token and what are the trade-offs?
advanced
localStorage is simple but readable by any XSS. An HttpOnly; Secure; SameSite cookie is not script-readable but needs CSRF defense. In-memory storage with a refresh via an HttpOnly cookie is a common compromise. The backend-for-frontend (BFF) pattern keeps tokens server-side entirely.
BFF: browser holds only a session cookie; the BFF calls APIs with tokens.
Short access-token lifetime limits damage in any model.
⚠ Follow-up traps
Is cookie storage immune to XSS? No, XSS can still issue requests with the cookie.
Does in-memory survive refresh? No, it must be re-obtained silently.
#jwt#spa#storage
Q69
A mobile app uses OAuth2. Which flow and why?
intermediate
Authorization code flow with PKCE using the system browser. The app cannot keep a client secret, so PKCE binds the code exchange to a one-time verifier.
Flow: generate code_verifier, send code_challenge (S256), later send code_verifier with the code.
Use claimed HTTPS redirect URIs or app links, not embedded web views.
⚠ Follow-up traps
Can the app embed a client secret? No, extractable from the binary.
What does PKCE protect against? Interception of the authorization code.
#oauth2#pkce#mobile
Q70
Two microservices need to call each other without a user. Which OAuth2 grant?
intermediate
Client credentials grant: the service authenticates to the authorization server with its ID and secret (or mTLS/private key JWT) and obtains an access token scoped to its permissions.
Spring also offers ShallowEtagHeaderFilter, which hashes the body: saves bandwidth but still runs the handler.
Never cache personalized responses as public.
⚠ Follow-up traps
Does a shallow ETag reduce CPU? No, the handler still executes.
How do clients get fresh data sooner? Use short max-age plus validators, or versioned URLs.
#caching#etag#scenario
Q72
A CDN served one user's profile to another user. What went wrong?
advanced
The response was cacheable by a shared cache (public, or no Cache-Control with heuristics), and the cache key ignored the identity (cookie or Authorization). Mark personalized responses Cache-Control: private or no-store, and set proper Vary.
Shared caches generally do not store responses to requests with Authorization unless allowed with public/s-maxage.
Cache-key mistakes also enable cache poisoning via unkeyed headers.
⚠ Follow-up traps
Is Vary: Cookie a good fix? It works but destroys hit rate; prefer private/no-store.
Does private stop the browser caching? No, only shared caches.
#caching#cdn#security
Q73
Two users edit the same resource and the last write silently wins. How do you prevent lost updates over HTTP?
advanced
Use optimistic concurrency with ETags: the client sends If-Match: "<version>" on PUT; the server compares to the current version and returns 412 Precondition Failed (or 428 if the header is required but missing).
@PutMapping("/docs/{id}")ResponseEntity<Doc> update(@PathVariable long id, @RequestHeader("If-Match") String etag, @RequestBody Doc d) { return ResponseEntity.ok(service.update(id, etag, d)); // throws on version mismatch}
Back it with a JPA @Version column.
⚠ Follow-up traps
412 or 409? 412 for failed preconditions; 409 for a state conflict generally.
Is the ETag derived from the body or version? Either; a version column is cheaper.
#etag#concurrency#optimistic-locking
Q74
A client expects `/users` to return XML but gets JSON. Why, and how do you support both?
basic
The client probably did not send Accept: application/xml, or no XML converter is on the classpath. Add jackson-dataformat-xml and let Spring negotiate on Accept.
Default order uses the first converter that supports the requested type; */* gets the default (JSON).
Query-parameter-based negotiation (?format=xml) is disabled by default in Spring 6 and must be configured.
⚠ Follow-up traps
Which status when no match exists? 406.
Why not rely on file extensions (/users.xml)? Extension-based negotiation is deprecated and removed in Spring 6.
#content-negotiation#spring-mvc
Q75
A request returns 415 Unsupported Media Type. How do you debug it?
basic
The Content-Type of the request is missing or not handled by the endpoint (consumes) or by any HttpMessageConverter. Check that the client sends Content-Type: application/json and that the mapping accepts it.
Posting a form (application/x-www-form-urlencoded) to a @RequestBody JSON endpoint gives 415.
Charset or typo errors (application/jsonn) cause it too.
⚠ Follow-up traps
400 versus 415 for bad JSON syntax? Bad syntax yields 400; wrong media type yields 415.
Is consumes required? No, but it clarifies and narrows matching.
#content-type#debugging
Q76
A filter reads the request body for logging, then the controller gets an empty body. What happened?
intermediate
getInputStream() can be consumed only once. Wrap the request in a caching wrapper so it can be read again.
ContentCachingRequestWrapper w = new ContentCachingRequestWrapper(req);chain.doFilter(w, res);byte[] body = w.getContentAsByteArray(); // available after the controller read it
ContentCachingRequestWrapper caches only what downstream actually reads; reading it up front requires a custom wrapper.
Log selectively and mask secrets; cap the logged size.
⚠ Follow-up traps
Can getReader() and getInputStream() both be used? No, IllegalStateException.
Why avoid logging bodies in prod? PII leakage and I/O overhead.
#filter#request-body
Q77
Your filter modifies headers after `chain.doFilter` and nothing changes. Why?
intermediate
The response was already committed (headers flushed) when the servlet wrote and flushed the body or filled the buffer. After that headers and status can no longer change.
Check response.isCommitted().
Set headers before chain.doFilter, or wrap the response (HttpServletResponseWrapper) and buffer the output.
Spring's OncePerRequestFilter plus ContentCachingResponseWrapper lets you inspect the body, then call copyBodyToResponse().
⚠ Follow-up traps
What is the default response buffer? Typically 8 KB in Tomcat.
Forgot copyBodyToResponse()? The client gets an empty body.
#filter#response-commit
Q78
A filter runs twice for one request (forward or error dispatch). How do you handle it?
intermediate
Filters mapped to multiple dispatcher types (REQUEST, FORWARD, ERROR, ASYNC) run for each dispatch. Extend OncePerRequestFilter, which tracks execution via a request attribute, and override shouldNotFilterAsyncDispatch/shouldNotFilterErrorDispatch as needed.
In Spring Boot, FilterRegistrationBean.setDispatcherTypes controls this.
Async requests are dispatched twice by design (initial and result dispatch).
⚠ Follow-up traps
Does OncePerRequestFilter apply across async dispatches by default? It skips async dispatch by default (shouldNotFilterAsyncDispatch returns true).
Do error-page forwards re-run security filters? They can, which is why error paths need access rules.
#filter#dispatch
Q79
A servlet stores a counter in an instance field and the count is wrong under load. Explain.
basic
Concurrent threads read-modify-write the shared field, losing updates. Use AtomicInteger or LongAdder, or avoid shared state.
public class CounterServlet extends HttpServlet { private final AtomicInteger hits = new AtomicInteger(); protected void doGet(HttpServletRequest rq, HttpServletResponse rs) throws IOException { rs.getWriter().println(hits.incrementAndGet()); }}
A plain int with hits++ is not atomic; synchronized on doGet would serialize all requests.
A per-node counter is wrong across a cluster; use a shared store.
⚠ Follow-up traps
Would volatile int fix it? No, ++ is still a non-atomic compound operation.
Is a Spring singleton controller different? No, same hazard.
#servlet#thread-safety
Q80
A user submits an order form, refreshes the page, and gets a duplicate order. How do you prevent it?
basic
Use Post/Redirect/Get: after processing the POST, respond with a 303 redirect to a confirmation GET page, so refresh repeats the harmless GET. For double-click and retries, add an idempotency or one-time form token.
@PostMapping("/orders")String create(OrderForm f, RedirectAttributes ra) { long id = service.create(f); ra.addFlashAttribute("msg", "Created"); return "redirect:/orders/" + id;}
⚠ Follow-up traps
Why flash attributes? They survive exactly one redirect, via the session.
Does PRG stop double clicks? No, a server-side token or unique constraint does.
#redirect#post-redirect-get
Q81
`request.getSession()` is creating sessions for every crawler hit and memory grows. What do you do?
intermediate
Use getSession(false) where a session is not needed, avoid creating sessions on public pages (JSPs create one by default unless <%@ page session="false" %>), shorten the timeout, and consider stateless endpoints.
Spring Security's SessionCreationPolicy.STATELESS stops creating sessions for APIs.
Rate-limit or filter bots; monitor active session counts.
⚠ Follow-up traps
Do JSPs create sessions silently? Yes, by default.
How to inspect the count? Tomcat JMX activeSessions or an HttpSessionListener.
#session#memory
Q82
Behind a load balancer, `request.getRemoteAddr()` shows the proxy IP and redirects use `http://`. Fix it.
intermediate
Configure the app to honour X-Forwarded-For/-Proto/-Host (or Forwarded) from trusted proxies.
server: forward-headers-strategy: native # or framework tomcat: remoteip: internal-proxies: "10\\.\\d+\\.\\d+\\.\\d+"
native uses the container's RemoteIpValve; framework uses Spring's ForwardedHeaderFilter.
Trust the headers only from your own proxies.
⚠ Follow-up traps
Why does a redirect use http://? The container sees plain HTTP from the proxy and builds absolute URLs from that.
Is the leftmost X-Forwarded-For entry reliable? No, clients can inject it; trust the rightmost untrusted-hop logic.
#proxy#forwarded-headers
Q83
A `sendRedirect` to a relative path breaks behind a context path or proxy. Why?
intermediate
sendRedirect("/x") is relative to the server root, not the application's context path, while sendRedirect("x") is relative to the current URL. Prefix request.getContextPath(), and behind a proxy ensure forwarded headers are processed.
Spring's redirect: prefix prepends the context path for /-leading URLs.
Redirects build absolute Location from the Host the container sees.
⚠ Follow-up traps
Does forward("/x") use the context path? Yes, forward paths are relative to the context root.
Open redirect risk? Never redirect to an unvalidated user-supplied URL.
#redirect#servlet
Q84
A long report endpoint takes 60 seconds and ties up a request thread. What are your options?
advanced
Make it asynchronous: return 202 Accepted with a Location to a status resource, process on a worker queue, and let the client poll or receive a webhook/SSE. If the client must wait, use DeferredResult/Callable.
Jobs belong in a durable queue so they survive restarts.
Proxy idle timeouts (often 60s) will cut long synchronous calls anyway.
⚠ Follow-up traps
Does Callable free resources for a slow DB query? It frees the container thread but occupies another one.
What does the status endpoint return when done? 200 with the result, or a redirect (303) to it.
#async#long-running
Q85
An async servlet request completes but the client gets an empty response or 500. What do you check?
advanced
Typical causes: complete() never called (timeout fires), asyncSupported missing on a filter or servlet, response written after completion, or the work thread threw before writing.
Register an AsyncListener with onTimeout and onError.
Set an explicit timeout (setTimeout); the default is container-specific (Tomcat 30s).
Ensure exceptions in the worker path always end with complete() or dispatch().
⚠ Follow-up traps
Which thread does complete() run on? Any; it is safe to call from the worker.
What if the client disconnects mid-way? Writing throws IOException; handle and complete.
#async#servlet#debugging
Q86
Server-Sent Events work locally but arrive in bursts behind Nginx. Why?
advanced
The proxy buffers upstream responses. Disable buffering for that route (proxy_buffering off; or the X-Accel-Buffering: no response header), disable compression for the stream, and keep idle timeouts above your heartbeat interval.
Send periodic comment lines (:\n\n) as keepalives.
SseEmitter timeout in Spring defaults to the async timeout; set it deliberately.
⚠ Follow-up traps
Does gzip affect SSE? It can buffer small events until a block fills.
WebSocket or SSE? SSE is one-directional over HTTP with auto-reconnect; WebSocket is bidirectional.
#sse#proxy#streaming
Q87
You must choose between Tomcat and Undertow for a new service. How do you decide?
intermediate
Default to Tomcat: best supported in Spring Boot, widest operational knowledge and tooling. Choose Undertow or Jetty for specific needs such as lower memory, particular WebSocket behavior, or existing expertise, and decide with a load test of your workload.
The server rarely dominates latency; the database and downstream calls do.
Check Jakarta EE/Servlet version compatibility with your Spring Boot version before switching.
⚠ Follow-up traps
Will switching servers speed up a blocking app? Marginally; the blocking model is unchanged.
How do you switch in Maven? Exclude spring-boot-starter-tomcat from spring-boot-starter-web and add the alternate starter.
#embedded#trade-offs
Q88
A service must handle 20,000 mostly idle WebSocket or long-poll connections. Which model fits?
advanced
Idle connections are cheap on non-blocking servers (Netty/WebFlux, Undertow, Jetty async) but would exhaust a thread-per-request pool if each holds a worker. With Tomcat NIO, idle WebSockets do not pin worker threads, but blocking handlers do; virtual threads also fit.
Size file descriptors (ulimit -n) and max-connections.
Plan memory per connection and heartbeat/idle-timeout policy.
Scale out with sticky routing or a broker (Redis, Kafka) for fan-out.
⚠ Follow-up traps
Does 20k connections require 20k threads in WebFlux? No, a handful of event-loop threads.
What kills it first usually? File descriptor limits or memory, not CPU.
#reactive#websocket#capacity
Q89
A WebFlux endpoint becomes unresponsive after adding a JDBC call. Why?
advanced
JDBC blocks the Netty event-loop thread. With only a few loops, a handful of slow queries stall all requests. Use R2DBC, or offload blocking calls to a bounded elastic scheduler.
If most of your stack is blocking, Spring MVC (with virtual threads) is a better fit.
⚠ Follow-up traps
Is .block() ever safe? Not on an event-loop thread; it throws or deadlocks.
Does subscribeOn make it non-blocking? No, it moves blocking to another pool.
#reactive#blocking
Q90
Your API has a breaking change for 30% of clients. How do you roll it out?
intermediate
Introduce /v2 (or a new media type) next to /v1, keep both running, announce deprecation with Deprecation and Sunset headers plus docs, track per-client usage of v1, and remove it only after the sunset date and usage drop.
Share business logic and diverge only in the web layer/DTO mapping.
Contract tests protect v1 behaviour.
Avoid forking databases per version.
⚠ Follow-up traps
Why not just change the response in place? It breaks clients you cannot redeploy.
Can feature flags replace versioning? For internal rollouts yes; for public contracts no.
#api-versioning#deprecation
Q91
Page 2000 of a listing endpoint takes seconds and users see duplicates when scrolling. Redesign it.
intermediate
Switch from offset to keyset pagination with a deterministic, indexed sort and a unique tiebreaker. Return an opaque cursor; both the cost and the duplicates/gaps problem disappear.
List<Order> next(Instant ts, long id, int size) { return repo.findNext(ts, id, PageRequest.ofSize(size)); // WHERE (created_at, id) < (?, ?)}
Add a composite index (created_at DESC, id DESC).
Keep offset only for small, bounded datasets or when random page access is required.
⚠ Follow-up traps
Can cursor pagination offer "go to page N"? Not directly.
Should cursors be tamper-proof? Treat as untrusted input; validate or sign them.
#pagination#performance
Q92
One client floods the API and degrades others. What do you implement?
intermediate
Per-client rate limiting with a token bucket in the gateway or a shared Redis store, returning 429 with Retry-After. Combine with request timeouts, bulkheads and concurrency limits so one tenant cannot consume all threads.
Key by API key or authenticated principal, not only IP (NAT sharing).
Tier limits by plan; log and alert on sustained throttling.
⚠ Follow-up traps
Is in-memory Bucket4j enough for 5 nodes? Each node limits separately, giving 5x the intended rate; use a distributed backend.
Does limiting by IP work behind a proxy? Only if you trust forwarded headers correctly.
#rate-limiting#scenario
Q93
Generated OpenAPI docs differ from real behavior and clients break. How do you keep them in sync?
intermediate
Make the spec part of CI: generate it from code (springdoc) and diff against the committed spec to catch breaking changes (openapi-diff), or go design-first and generate server interfaces so code cannot drift. Add contract tests (Spring Cloud Contract, Pact, schema validation).
Annotate error responses and security schemes, not just success cases.
Fail the build on unreviewed contract changes.
⚠ Follow-up traps
Code-first or design-first? Design-first for public contracts, code-first for fast internal iteration.
Does passing unit tests ensure contract fidelity? No, they do not exercise serialization-level shape.
#openapi#contract
Q94
Validation errors return a 500 with a stack trace. How do you fix it properly?
basic
Validate with Bean Validation (@Valid) and map MethodArgumentNotValidException to a 400 ProblemDetail in a central @RestControllerAdvice.
Why not validate in the DB only? Late failures give poor messages and waste work.
Where do filter exceptions go? To the container error page (/error), not to @ControllerAdvice.
#validation#error-handling
Q95
A client reports intermittent `Connection reset` or 502 from the gateway during idle periods. What could be going on?
advanced
A keep-alive timeout mismatch: the backend closes an idle connection just as the proxy reuses it. The proxy's upstream idle timeout must be shorter than the server's keep-alive timeout.
Tomcat keepAliveTimeout defaults to the connection timeout (20s in Boot); load balancers (e.g. AWS ALB 60s) idle timeouts should sit below backend timeouts.
Enable retries for idempotent requests only.
⚠ Follow-up traps
Is it safe for a proxy to retry a POST? Not unless the operation is idempotent.
Which side should have the longer idle timeout? The backend.
#keep-alive#timeouts#proxy
Q96
Responses are large JSON and bandwidth is high. What improvements apply?
basic
Enable response compression (gzip or Brotli), paginate, support field selection, and cache with validators.
Do not compress already compressed content (images, zip).
Compression costs CPU and can interact with TLS attacks (BREACH) when secrets are reflected in responses.
⚠ Follow-up traps
Who compresses best, app or proxy? Usually the proxy/CDN to keep app threads free.
What header drives it?Accept-Encoding request and Content-Encoding/Vary response.
#compression#performance
Q97
A request with `X-HTTP-Method-Override` or a form `_method` hits DELETE logic. Is that a problem?
advanced
Method override lets HTML forms (GET/POST only) simulate PUT/DELETE; Spring's HiddenHttpMethodFilter honours _method on POST. It is a risk if CSRF/authorization rules are applied by the original method, or if caches and proxies treat the POST differently than the logic does.
Apply authorization after override resolution (the filter runs early in the chain).
Disable the filter in pure API services (it is off by default in Boot 2.2+).
⚠ Follow-up traps
Can a GET override to DELETE? The filter only honours POST requests.
Why do APIs prefer true verbs? Caches, retries and security tooling depend on semantics.
#http-methods#security
Q98
A `GET /search` endpoint performs a write (logs a purchase). What can go wrong?
intermediate
GET is assumed safe: browsers prefetch, crawlers follow links, proxies cache and retry it. Side effects will fire unexpectedly and duplicates can occur. Move state changes to POST/PUT/DELETE.
Sensitive parameters in GET URLs leak into logs, history and Referer headers.
Analytics-only writes are acceptable if truly harmless, but make them asynchronous.
⚠ Follow-up traps
Can GET have a body? Allowed by the spec but unreliable; avoid it, use POST for complex searches or the QUERY method draft.
URL length limits? Practical limits around 2-8 KB depending on server and proxy.
#http-methods#safe-methods
Q99
Deleting a resource twice returns 204 and then 404. Is DELETE still idempotent?
basic
Yes. Idempotency concerns server state: after one or many DELETEs the resource is gone. The response code may differ between calls.
Returning 204 on a repeat is also valid and friendlier to retrying clients.
Soft deletes remain idempotent if the flag is simply set.
⚠ Follow-up traps
Is POST ever idempotent? Only by design (idempotency keys), not by definition.
Is a counter-increment PUT idempotent? If the body says value=5, yes; if the server increments, no.
#idempotency#delete
Q100
Users complain they are redirected to the login page after a form post in a Spring Security app with an expired session. How do you improve this?
intermediate
Handle expiry explicitly: configure an invalid-session strategy that redirects with a message, return 401 JSON for AJAX calls (via an AuthenticationEntryPoint), and use a session timeout with client-side warnings or silent refresh for long forms.
Remember-me tokens can restore authentication, with persistent token storage.
Pending form data is lost; save drafts client-side if needed.
⚠ Follow-up traps
Does user activity extend the session? Yes, each request resets idle time.
Why a 302 to the login page is bad for fetch calls? The client follows it and gets HTML instead of an error status.
#session#spring-security#timeout
Q101
After enabling Spring Security, all POST requests from your frontend return 403. Why?
basic
CSRF protection is on by default and the requests lack the token. Include it (form hidden field, X-CSRF-TOKEN or X-XSRF-TOKEN header), or disable CSRF only for stateless token-authenticated APIs.
http.csrf(csrf -> csrf.disable()); // only for stateless bearer-token APIs
Thymeleaf forms add the token automatically with th:action.
GET requests succeed since they are not protected.
⚠ Follow-up traps
Is disabling CSRF acceptable with cookie sessions? No.
Why only POST failing? CSRF checks apply to unsafe methods.
#csrf#spring-security
Q102
How would you design secure file download and avoid path traversal?
advanced
Never build file paths from raw input. Look up files by ID in a database or resolve against a base directory and verify the normalized path stays inside it.
Path base = Path.of("/data/files").toRealPath();Path p = base.resolve(name).normalize();if (!p.startsWith(base)) throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
Send Content-Disposition: attachment; filename*=UTF-8''... and a safe Content-Type.
Authorize per file and stream using Resource/StreamingResponseBody; support Range for resumption.
⚠ Follow-up traps
Does .. filtering suffice? No, encoded variants and symlinks bypass naive checks.
Why normalize() before startsWith? Otherwise base/../etc still starts with base lexically.
#file-download#security#path-traversal
Q103
In a Spring Boot app, upload of a 5 MB file returns 413 or `MaxUploadSizeExceededException`. What do you change?
basic
Raise the multipart limits, the defaults being 1 MB per file and 10 MB per request, and also any proxy limit.
Nginx: client_max_body_size; Tomcat has maxPostSize for form posts (not multipart parts).
Handle MaxUploadSizeExceededException with a 413 ProblemDetail.
⚠ Follow-up traps
Which wins if the proxy limit is lower? The proxy rejects first with its own 413.
Is the exception always catchable by @ControllerAdvice? Not if raised in the container's multipart parsing before MVC; it depends on resolver timing.
#file-upload#configuration
Q104
Design an authentication and authorization setup for a public REST API consumed by a SPA and partner services.
advanced
Use an OIDC authorization server. The SPA uses authorization code + PKCE (or a BFF); partners use client credentials. The API is an OAuth2 resource server validating JWTs (signature, iss, aud, exp) and enforcing scopes plus object-level checks.