An application server runs the business logic (the database calls, the auth checks, the API endpoints). A web server serves static files and proxies requests to the application server. The line blurs when one tool tries to be both, which is most modern servers.
Table of contents
- The original distinction
- The blurred line
- The right architecture for most apps
- When one server is enough
- The Java world
- The application server in the cloud-native world
- The migration path from monolith to microservices
- FAQ
The original distinction
In the 2000s, the distinction was sharp:
- Web server (Apache, nginx): Serves HTML, CSS, JS, images. Doesn’t run application code.
- Application server (Tomcat, JBoss, WebLogic): Runs Java/Python/Ruby/Node code. Doesn’t serve static files efficiently.
The architecture: nginx in front, Tomcat behind. Nginx serves static files; nginx proxies dynamic requests to Tomcat. Tomcat runs the application code.
The blurred line
Modern servers blur the line:
- nginx can run Lua scripts (OpenResty).
- Apache can run PHP via
mod_php. - Node.js is both: it serves static files and runs application code.
- Python (Flask, FastAPI) is both: serves static files via
StaticFilesand runs application code via the route handlers.
The team that picks a tool that does both is fine for small apps; the team that picks a tool that does both for a large app usually regrets it.
The right architecture for most apps
The right pattern for a modern web app:
- Reverse proxy (nginx, Caddy, or a managed load balancer). Handles TLS, serves static files, rate limits, routes requests.
- Application server (FastAPI, Django, Express, Rails). Runs the business logic.
- Database (Postgres, MySQL, MongoDB). Stores the data.
The reverse proxy is the right place to do TLS termination; the application server should not have to deal with certificates. The team that puts TLS on the application server is the team that has a hard time scaling.
When one server is enough
For a small app (a single-page app, a static site, a low-traffic API), one server can do everything. The team that uses FastAPI to serve both the API and the static frontend files has a simpler architecture; the team that adds nginx in front of FastAPI for the same workload has more moving parts for no benefit.
The line is when the application logic starts to dominate the request time. The team that sees nginx serving 80% of the traffic as static files and the app serving 20% has a single-server architecture that scales. The team that sees nginx serving 5% and the app serving 95% has a single-server architecture that needs to split.
The Java world
Java still uses the strict distinction:
- Apache Tomcat / Jetty: Application server (servlet container).
- Apache HTTP Server / nginx: Web server (front of Tomcat).
- WildFly / JBoss / WebLogic: Full Java EE application server with EJB, JMS, etc.
The team that uses Spring Boot with embedded Tomcat has the application server and web server in one process, which is a common pattern in modern Java.
The application server in the cloud-native world
The traditional application server (Tomcat, JBoss) is being replaced by:
- Container-based deployments. A Dockerfile that runs the app in a container. Deployed to Kubernetes, ECS, or a PaaS.
- Serverless functions. AWS Lambda, Google Cloud Functions, Azure Functions. The cloud provider runs the function; no application server to manage.
- Static-site + API split. The frontend is a static SPA; the backend is a set of serverless functions or a small container.
The team that uses containers has the most flexibility. The team that uses serverless has the least operational overhead. The team that uses static + API has the best performance for content-heavy apps.
The migration path from monolith to microservices
The right way to break a monolith into services:
- Identify bounded contexts. The team that has a 10-year-old monolith usually has 3-5 clear bounded contexts (user management, billing, content, search, notifications).
- Extract one context at a time. Start with the smallest, most independent one (usually notifications or search). Deploy as a separate service.
- Use the strangler pattern. The monolith stays running; new service handles specific routes. Over time, the monolith shrinks as more routes migrate.
- Don’t share a database. Each service has its own database. Cross-service data goes through APIs, not direct database access.
The team that uses the strangler pattern has a smooth migration. The team that rewrites the entire monolith at once ships late (or never).
FAQ
Can nginx run an application?
Yes, with Lua (OpenResty) or njs (nginx JavaScript). The team that wants to run a real application should use a real application server, not nginx with Lua.
Is Node.js an application server or a web server?
Both. Node.js with Express serves static files (with express.static) and runs application code (with route handlers). For most use cases, this is fine.
What about serverless?
Serverless replaces the application server. The team that uses Lambda or Cloud Functions doesn’t run an application server; the cloud provider runs the function when the request comes in. The web server (or API gateway) is still there.
Should I use nginx in front of Node.js?
For production, usually yes: TLS termination, rate limiting, static file serving. The team that puts nginx directly in front of Node.js gets the right architecture for most apps.
What’s the difference between application server and web server?
Application server runs the business logic; web server serves static files and proxies dynamic requests. In modern architectures, the two are often combined (Node.js, Python with StaticFiles).
Do I need an application server in 2026?
Depends on the architecture. Serverless functions replace the application server for many use cases. Containers in Kubernetes replace it for steady-state workloads. The team that uses a managed PaaS doesn’t think about application servers.
What about WebSphere, WebLogic, JBoss?
Legacy Java EE application servers. Most modern Java uses Spring Boot with embedded Tomcat or Jetty, which is much lighter. The team that inherits a legacy WebSphere deployment has a migration project.
If you are sizing the infrastructure for the kind of project this post covers, the RunxBuild hosting calculator is the right place to model the line items. The compute, the memory, the storage, the bandwidth, the database - each one is a separate number, and the team’s mental model for the platform is the sum of those numbers. The RunxBuild dashboard is where the team sees the actual usage in one place.
Useful related references: