Skip to main content
ExamExplained
NSW · Software Engineering
Software Engineering study scene
§-Syllabus dot point
NSWSoftware EngineeringSyllabus dot point

Inquiry Question 1: How are secure web applications developed?

Describe the client-server architecture of the web, including the roles of the browser, web server, application server and database

A focused answer to the HSC Software Engineering Module 2 dot point on web architecture. Browser, web server, application server, database, the request-response cycle, the worked three-tier example, and the traps markers look for.

Reviewed by: AI editorial process; not yet individually human-reviewed

Have a quick question? Jump to the Q&A page

What this dot point is asking

NESA wants you to describe how a modern web application is split across several tiers - browser, web server, application server, database - and explain what each does. The request-response cycle is the canonical worked example.

The answer

A modern web application is split across four tiers, with the browser on the user's device and the other three on the server side. The diagram shows the path of one request and response.

Four-tier web application architecture Four boxes left to right represent the browser, web server, application server, and database, each shaded a distinct colour. Arrows along the top show a request flowing right; arrows along the bottom show the response flowing left. The browser is on the client side; the other three tiers are on the server side, separated by a dashed boundary. Browser HTML, CSS, JS user device Web server Nginx, Apache HTTPS, static Application server Flask, Node, Spring business logic Database PostgreSQL, MySQL persistent data request response client side server side Only the application server talks to the database; the browser never connects to it directly.

The four tiers

Most web applications follow a three- or four-tier architecture:

  • Client (browser): runs HTML, CSS, JavaScript. Displays the UI. Sends HTTP requests in response to user actions.
  • Web server: handles HTTPS termination, serves static files, routes dynamic requests to the application server. Examples: Nginx, Apache, Caddy.
  • Application server: runs the business logic. Reads input, applies authorisation, queries the database, formats output. Examples: a Python Flask app, a Node.js Express server, a Java Spring service.
  • Database: persistent storage. Relational (PostgreSQL, MySQL, SQLite) or NoSQL (MongoDB, DynamoDB). Only the application server connects to it directly.

Some setups split or merge these tiers (single-page apps with a separate API tier, serverless functions that combine web and application server, monoliths that bundle web and application server).

The request-response cycle

  1. The user clicks "Add to cart".
  2. The browser issues a POST request: POST /api/cart HTTP/1.1 Host: shop.example.com Content-Type: application/json Body: {"product_id": 42}.
  3. The web server receives the request, terminates HTTPS, and forwards it to the application server.
  4. The application server authenticates the session token, validates the product ID, and runs the handler.
  5. The handler calls the database: INSERT INTO cart_items (user_id, product_id) VALUES (?, ?).
  6. The database executes the query and returns success.
  7. The handler returns a 201 response with the new cart contents as JSON.
  8. The web server forwards the response to the browser.
  9. The browser updates the UI based on the JSON response.

Most pages involve multiple requests: the initial HTML, several CSS and JavaScript files, images, fonts, and any AJAX calls JavaScript makes.

A worked code example

A minimal three-tier slice in Python with Flask:

from flask import Flask, request, jsonify
import sqlite3

app = Flask(__name__)

def db():
    return sqlite3.connect("shop.db")

@app.post("/api/cart")
def add_to_cart():
    user_id = authenticate(request.headers.get("Authorization"))
    if not user_id:
        return jsonify(error="unauthenticated"), 401
    data = request.get_json()
    product_id = int(data["product_id"])
    with db() as conn:
        conn.execute(
            "INSERT INTO cart_items (user_id, product_id) VALUES (?, ?)",
            (user_id, product_id),
        )
    return jsonify(status="ok"), 201

In front of this app, Nginx (web server) handles HTTPS and serves static files. Behind it, SQLite (database) stores cart data.

Why split into tiers

  • Separation of concerns: each tier has a focused job and uses tools optimised for that job.
  • Scalability: each tier can scale independently. If the database is the bottleneck, scale the database. If application logic is the bottleneck, run more application server instances.
  • Security: only the application server reaches the database. The database is not exposed to the internet.
  • Maintainability: changes to the UI (browser tier) do not force changes to the database schema.

Exam-style practice questions

Practice questions written in the style of NESA exam questions on this dot point, with worked answer explainers. The year tag is the paper they imitate, not the source.

2024 HSC5 marksExplain the roles of the browser, web server, application server and database in a typical web application. Trace the path of a single user request from click to displayed page.
Show worked answer →

The four roles in a typical three-tier web app:

Browser (client): renders HTML, runs JavaScript, sends HTTP requests, displays responses. The user's window into the system.

Web server: terminates HTTPS, serves static assets (HTML, CSS, JavaScript, images), and forwards application requests to the application server. Examples: Nginx, Apache.

Application server: runs the business logic (the developer's code in Python, Node, Ruby, Java). Reads and writes the database, applies validation and authorisation, returns dynamic responses.

Database: stores persistent data. Relational (PostgreSQL, MySQL) or NoSQL (MongoDB). Only the application server talks to it.

Trace of one request:

  1. User clicks a link. The browser issues a GET request over HTTPS.
  2. The request reaches the web server, which serves the page shell or forwards the call to the application server.
  3. The application server authenticates the request, runs the handler, queries the database with a parameterised SELECT.
  4. The database returns the rows.
  5. The application server renders the response (HTML or JSON) and returns it to the web server.
  6. The web server forwards the response to the browser.
  7. The browser parses HTML, fetches CSS/JS/images, runs JavaScript, displays the page.

Markers reward all four roles, named tiers in the right order, and an end-to-end trace including the database round trip.

Practice questions

Original practice questions graded from foundation to exam level, each with a full worked solution. Try them before revealing the solution.

foundation3 marksMatch each description to a tier: (a) 'stores rows for the orders table', (b) 'runs the validate_order() function and checks the user's permissions', (c) 'renders the checkout button and sends the click as an HTTP request'.
Show worked solution →

(a) Database - persistent storage of rows.

(b) Application server - business logic, validation and authorisation.

(c) Browser (client) - rendering the UI and sending HTTP requests.

Marking criteria: 1 mark for each correct tier identification (3 total), matched to the defining job of that tier rather than a guess.

foundation3 marksExplain why the browser must never connect directly to the database in a well-designed three-tier web application.
Show worked solution →

The database usually holds sensitive and complete data (every user's records, not just what one page needs), and it has no way on its own to check whether a given browser request is authorised or well-formed. If the browser could connect directly, an attacker could bypass all of the application server's validation and authorisation logic and query or modify data with no controls at all. Keeping the database reachable only from the application server (on a private network) means every access path is forced through one place that enforces authentication, authorisation and input validation.

Marking criteria: 1 mark for identifying the security risk of exposing raw data, 1 mark for identifying that validation/authorisation would be bypassed, 1 mark for stating that restricting access to the application server centralises control.

core4 marksA hosting provider's firewall rule table for a web app reads: | Source | Destination | Port | Allowed? | |---|---|---|---| | Internet | Web server | 443 | Yes | | Internet | Database | 5432 | Yes | | Web server | Application server | 8000 | Yes | | Application server | Database | 5432 | Yes | Identify the misconfigured rule and explain the risk it creates.
Show worked solution →

The misconfigured rule is "Internet to Database, port 5432, Allowed". This exposes the database's connection port directly to the public internet, bypassing the application server entirely. An attacker could attempt to connect straight to the database, try default or leaked credentials, or exploit a database vulnerability, without going through any of the application server's authentication, authorisation or input-validation logic.

The rule should instead deny internet access to port 5432 and allow it only from the application server's own address, matching the "application server to database" rule already present in the table.

Marking criteria: 1 mark for identifying the correct row, 1 mark for explaining that it exposes the database to the public internet, 1 mark for explaining the consequence (bypassing application-layer controls), 1 mark for stating the correct fix (restrict to application-server-only access).

core5 marksTrace a user login request across all four tiers, from the moment the user submits the login form to the moment they see the logged-in home page.
Show worked solution →
  1. Browser: the user submits the login form; JavaScript sends a POST request over HTTPS with the username and password in the JSON body.
  2. Web server: terminates the HTTPS connection and forwards the request to the application server, since it is not a static file.
  3. Application server: validates the input, looks up the username, and checks the submitted password against the stored password hash.
  4. Database: the application server queries the users table (a parameterised SELECT) and returns the matching row (or none) to the application server.
  5. Application server: on a match, creates a session/token, and returns a success response (e.g. 200 OK with a session cookie or token) via the web server.
  6. Browser: stores the token/cookie, then requests the home page data, which repeats the cycle to render the logged-in view.

Marking criteria: 1 mark for correctly starting at the browser with an HTTPS POST, 1 mark for the web server's forwarding role, 1 mark for the application server performing validation and the credential check, 1 mark for the database round trip with a parameterised query, 1 mark for the response path back through the web server to the browser and the resulting rendered page.

core4 marksExplain what it means for HTTP to be stateless, and where a web application actually keeps track of a user being logged in across multiple page loads.
Show worked solution →

HTTP is stateless: each request is a self-contained unit, and by default the web server and application server retain no memory of a previous request when a new one arrives. Without any extra mechanism, the server would have no way to know that two requests came from the same logged-in user.

To track login state across requests, the application typically issues a session identifier (a cookie) or a token (e.g. a JWT) after login. The browser stores it and sends it with every subsequent request (e.g. in a cookie header or an Authorization header). The application server looks up (or verifies) this identifier against session data kept server-side (often in the database or a fast in-memory session store) or, for a signed token, verifies it directly, to know who the request is from.

Marking criteria: 1 mark for a correct definition of statelessness, 1 mark for explaining the consequence (the server cannot tell requests apart without help), 1 mark for identifying a cookie/token as the mechanism, 1 mark for correctly stating where the actual session data or verification logic lives.

exam6 marksA school's online exam-booking system runs perfectly for a class of 30, but on the day bookings open for the whole cohort of 2000 students, the single Flask process (which also serves static files and hosts the database on the same machine) becomes unresponsive. Justify, using the four-tier model, a redesign that would handle this load, and explain what specifically would improve.
Show worked solution →

This is a 6-mark justify: markers reward a redesign tied explicitly to why each change addresses the described bottleneck, not just a generic list of "best practices".

Diagnosis. Bundling the web server, application server and database into one process means every request competes for the same CPU, memory and disk I/O, and a spike from 30 to 2000 concurrent users multiplies load on all three jobs at once with no way to relieve any single one.

Redesign and justification.

  • Separate the web server. Put Nginx in front to serve static assets (HTML/CSS/JS/images) directly, freeing the application process to spend all of its time on booking logic rather than file I/O for every request.
  • Scale the application server horizontally. Run several Flask (or equivalent) worker processes/instances behind a load balancer, so the 2000 concurrent booking requests are spread across multiple workers instead of queuing behind one process.
  • Move the database to its own machine/service. Isolating the database means booking-logic CPU spikes on the application tier no longer starve the database of resources, and the database itself can be tuned (connection pooling, indexes on the bookings table) independently.
  • Add a queue or rate limit for the booking write. Since many students will try to book the same limited slots simultaneously, a short queue or per-slot lock at the application/database layer avoids double-booking without needing to scale to "instant" throughput.

Overall improvement. Each tier can now be scaled or tuned to match its own bottleneck (more web server capacity for static files, more application workers for booking logic, database tuning for write contention), instead of one overloaded process being the ceiling for the whole system.

Marker's note: full marks require (1) correctly diagnosing that bundling all tiers creates a single shared bottleneck, (2) at least three concrete tier-specific changes (web server offload, application server scaling, database isolation), each justified against the described symptom, not just named, and (3) an explicit link back to why separating tiers helps under this specific load spike.

exam7 marksEvaluate the claim: 'A four-tier architecture is always the better choice over a single-process monolith for a new web application.' Use a concrete scenario in your answer.
Show worked solution →

This is a 7-mark evaluate: markers reward a balanced argument that reaches a reasoned judgement, not a one-sided answer.

Case for the four-tier split
For an application expecting significant or unpredictable traffic (an exam-booking system, a retail site during a sale), separating tiers allows independent scaling (more application server instances without touching the database), stronger security (the database is never internet-facing), and independent development (front-end and back-end teams can work without blocking each other). A real production system serving thousands of concurrent users benefits clearly from this separation, as shown by the exam-booking overload scenario where bundling every tier into one process became the bottleneck.
Case against, for a small or early-stage application
A small class project, prototype, or low-traffic internal tool (say, under a few dozen users) gains little from the operational complexity of four separately deployed, monitored and secured tiers. A single Flask process serving both static files and the API, backed by an embedded SQLite database, is simpler to build, deploy and debug, and the performance and security benefits of full separation are not yet needed because there is no load or attacker surface large enough to expose the monolith's weaknesses.
Judgement
The claim is too strong: four-tier separation is the better choice once traffic, security requirements, or team size make a monolith's limits a real (not hypothetical) problem, but for a small or early-stage system the extra infrastructure cost of full separation outweighs benefits it does not yet need. The right architecture depends on expected scale and risk, not a fixed rule.

Marker's note: top-band answers (1) argue both sides with a genuine scenario each, not just assertion, (2) tie the "for" case to a specific mechanism (independent scaling, database isolation) rather than vague praise, (3) tie the "against" case to a specific small-scale scenario where the extra complexity is not justified, and (4) close with an explicit, non-neutral judgement rather than "it depends" alone.

ExamExplained