<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>learn.kevalabs</title>
    <subtitle>Practical, first-principles guides on Rust, systems engineering, and applied AI — from the kevalabs software lab.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://learn.kevalabs.com/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://learn.kevalabs.com"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-08-16T00:00:00+00:00</updated>
    <id>https://learn.kevalabs.com/atom.xml</id>
    <entry xml:lang="en">
        <title>PostgreSQL 101: a quick introduction</title>
        <published>2026-08-16T00:00:00+00:00</published>
        <updated>2026-08-16T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://learn.kevalabs.com/databases/postgresql-101/"/>
        <id>https://learn.kevalabs.com/databases/postgresql-101/</id>
        
        <content type="html" xml:base="https://learn.kevalabs.com/databases/postgresql-101/">&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt; none — this quick introduction stands on its own.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Where this fits:&lt;/strong&gt; a full database course covers the theory, SQL in depth, and design. These notes are the &lt;em&gt;practical minimum&lt;/em&gt; to start building: what the pieces are called, which tools talk to the database, and why it rejects bad data. That&#39;s enough to model real applications.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The difference between a spreadsheet and a database is one word: &lt;strong&gt;rules&lt;/strong&gt;. A spreadsheet accepts whatever you type — a name in the age column, two students with the same ID, a blank where an email must be. A database lets you &lt;em&gt;declare the rules once&lt;/em&gt;, then refuses every violation, forever, no matter which app or teammate does the writing. Everything in this guide is about where data lives and how those rules work.&lt;/p&gt;
&lt;h2 id=&quot;the-containers-server-database-schema-table&quot;&gt;The containers: server → database → schema → table&lt;/h2&gt;
&lt;p&gt;PostgreSQL names trip people up because &lt;em&gt;database&lt;/em&gt; means both the software and one specific container inside it. There are four levels of nesting, and each has one job:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-pg-hierarchy&gt;&lt;/sim-pg-hierarchy&gt;&lt;/div&gt;
&lt;p&gt;The practical version: &lt;strong&gt;one project = one database&lt;/strong&gt; (your application&#39;s config points at exactly one), and unless you say otherwise everything lands in the default schema, &lt;strong&gt;&lt;code&gt;public&lt;/code&gt;&lt;/strong&gt;. When you meet &lt;code&gt;university.public.students&lt;/code&gt;, read it right-to-left: the &lt;code&gt;students&lt;/code&gt; table, in the &lt;code&gt;public&lt;/code&gt; schema, in the &lt;code&gt;university&lt;/code&gt; database.&lt;/p&gt;
&lt;h2 id=&quot;tables-columns-are-a-contract&quot;&gt;Tables: columns are a contract&lt;/h2&gt;
&lt;p&gt;A table is defined by its &lt;strong&gt;columns&lt;/strong&gt; — each with a name and a &lt;strong&gt;type&lt;/strong&gt; — and filled with &lt;strong&gt;rows&lt;/strong&gt;, one per record. Here&#39;s a real one:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot; &gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;CREATE TABLE&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; students&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    id     &lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;integer      PRIMARY KEY&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    name   varchar&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;100&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; NOT NULL&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    email  &lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;varchar&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;254&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The types are already rules: &lt;code&gt;integer&lt;/code&gt; refuses &lt;code&gt;&quot;twenty&quot;&lt;/code&gt;, &lt;code&gt;varchar(100)&lt;/code&gt; refuses a 300-character name. Columns are the &lt;em&gt;contract&lt;/em&gt;; rows are the data that signed it.&lt;/p&gt;
&lt;h2 id=&quot;constraints-the-table-defends-itself&quot;&gt;Constraints: the table defends itself&lt;/h2&gt;
&lt;p&gt;Types catch the wrong &lt;em&gt;kind&lt;/em&gt; of data. &lt;strong&gt;Constraints&lt;/strong&gt; catch data that&#39;s the right kind but still wrong:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;PRIMARY KEY&lt;/code&gt;&lt;/strong&gt; — this column is each row&#39;s identity: it must be &lt;strong&gt;unique&lt;/strong&gt; and can never be missing. Two students with id &lt;code&gt;1&lt;/code&gt;? Rejected.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;NOT NULL&lt;/code&gt;&lt;/strong&gt; — this value is required. A student with no name? Rejected.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Try to break the table yourself:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-constraints&gt;&lt;/sim-constraints&gt;&lt;/div&gt;
&lt;p&gt;The deep lesson: the database rejects bad data &lt;strong&gt;at the gate&lt;/strong&gt;, at write time — instead of you discovering it months later and cleaning up. In a project with four teammates and three apps touching the same data, the table&#39;s own rules are the only ones everyone is forced to obey.&lt;/p&gt;
&lt;h2 id=&quot;pgadmin-and-the-engine-a-query-s-journey&quot;&gt;pgAdmin and the engine: a query&#39;s journey&lt;/h2&gt;
&lt;p&gt;So far we&#39;ve talked about the server as a black box. Two things open it up: knowing what a &lt;strong&gt;client&lt;/strong&gt; is, and knowing what happens inside the &lt;strong&gt;engine&lt;/strong&gt; when a query arrives.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;pgAdmin&lt;/strong&gt; is the standard GUI client for PostgreSQL — a browsable tree of exactly the containers from the first simulator (server → databases → schemas → tables), plus a Query Tool where you type SQL. The crucial thing to understand: &lt;strong&gt;pgAdmin has no special powers.&lt;/strong&gt; When you press F5 in its Query Tool, it sends your SQL over an ordinary network connection (port 5432, the PostgreSQL wire protocol) — the same connection that &lt;code&gt;psql&lt;/code&gt; on the command line uses, and the same one your application or ORM uses. Every client is just a different way to put SQL on that wire.&lt;/p&gt;
&lt;p&gt;On the server side, every query — no matter which client sent it — passes through the same pipeline of engine components:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-pg-engine&gt;&lt;/sim-pg-engine&gt;&lt;/div&gt;
&lt;p&gt;Four components worth naming:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Parser&lt;/strong&gt; — checks the SQL&#39;s grammar and turns text into a query tree. Typos die here.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Planner/optimizer&lt;/strong&gt; — decides &lt;em&gt;how&lt;/em&gt; to run it: scan the whole table, or jump straight to rows via an index? (Primary keys get an index automatically — one more thing &lt;code&gt;PRIMARY KEY&lt;/code&gt; buys you.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Executor&lt;/strong&gt; — runs the chosen plan step by step.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Storage&lt;/strong&gt; — shared memory buffers in front of the actual data files on disk; the executor reads and writes through them, and constraints are enforced right here at write time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Keep this picture and two debugging superpowers follow: a &lt;em&gt;syntax error&lt;/em&gt; means you never got past the parser; a &lt;em&gt;slow query&lt;/em&gt; means the planner chose an expensive plan — different problems, different fixes.&lt;/p&gt;
&lt;h2 id=&quot;takeaways&quot;&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Read the nesting right-to-left&lt;/strong&gt;: table ⊂ schema (&lt;code&gt;public&lt;/code&gt; by default) ⊂ database (one per project) ⊂ server.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Columns + types are a contract&lt;/strong&gt;; rows are records that must honor it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PRIMARY KEY = unique + not null identity&lt;/strong&gt; for every row; &lt;strong&gt;NOT NULL = required field&lt;/strong&gt;. Declare rules once, they&#39;re enforced forever.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reject at the gate beats clean up later&lt;/strong&gt; — constraints are the only rules every app and teammate must obey.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pgAdmin is just a client.&lt;/strong&gt; GUI, &lt;code&gt;psql&lt;/code&gt;, and your app all speak the same protocol to the same engine: parser → planner → executor → storage.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When your app&#39;s ORM throws an &lt;code&gt;IntegrityError&lt;/code&gt;-style exception&lt;/strong&gt;, that&#39;s a constraint firing — the database doing its job, not the framework being difficult.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Codd, E. F. (1970). A relational model of data for large shared data banks. &lt;em&gt;Communications of the ACM, 13&lt;/em&gt;(6), 377–387. https://doi.org/10.1145/362384.362685&lt;/li&gt;
&lt;li&gt;The pgAdmin Development Team. (n.d.). &lt;em&gt;pgAdmin 4 documentation&lt;/em&gt;. Retrieved August 16, 2026, from https://www.pgadmin.org/docs/&lt;/li&gt;
&lt;li&gt;The PostgreSQL Global Development Group. (n.d.). &lt;em&gt;Constraints&lt;/em&gt; (PostgreSQL documentation, Chapter 5.5). Retrieved August 16, 2026, from https://www.postgresql.org/docs/current/ddl-constraints.html&lt;/li&gt;
&lt;li&gt;The PostgreSQL Global Development Group. (n.d.). &lt;em&gt;Overview of PostgreSQL internals&lt;/em&gt; (PostgreSQL documentation). Retrieved August 16, 2026, from https://www.postgresql.org/docs/current/overview.html&lt;/li&gt;
&lt;li&gt;The PostgreSQL Global Development Group. (n.d.). &lt;em&gt;Schemas&lt;/em&gt; (PostgreSQL documentation, Chapter 5.10). Retrieved August 16, 2026, from https://www.postgresql.org/docs/current/ddl-schemas.html&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Server-side rendering in pure Python, part 2: rendering from PostgreSQL</title>
        <published>2026-08-16T00:00:00+00:00</published>
        <updated>2026-08-16T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://learn.kevalabs.com/python/server-side-rendering-in-pure-python-part-2/"/>
        <id>https://learn.kevalabs.com/python/server-side-rendering-in-pure-python-part-2/</id>
        
        <content type="html" xml:base="https://learn.kevalabs.com/python/server-side-rendering-in-pure-python-part-2/">&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt; &lt;a href=&quot;/python/server-side-rendering-in-pure-python/&quot;&gt;part 1&lt;/a&gt; (this builds directly on its code) and &lt;a href=&quot;/databases/postgresql-101/&quot;&gt;PostgreSQL 101&lt;/a&gt; (databases, tables, constraints, pgAdmin). Time: ~45 minutes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Part 1 had one honest weakness: &lt;code&gt;STUDENTS&lt;/code&gt; was a Python list &lt;em&gt;frozen into the program&lt;/em&gt;. Add a student? Edit the source, restart the server. Real applications don&#39;t work that way — the data lives in a database, and the server renders &lt;strong&gt;whatever is there right now&lt;/strong&gt;. Today the fixed list dies. The plan: same server, same template, but &lt;code&gt;render()&lt;/code&gt; gets its rows from PostgreSQL, per request.&lt;/p&gt;
&lt;h2 id=&quot;set-up-the-database&quot;&gt;Set up the database&lt;/h2&gt;
&lt;p&gt;Using &lt;code&gt;psql&lt;/code&gt; or pgAdmin&#39;s Query Tool (this is the &lt;a href=&quot;/databases/postgresql-101/&quot;&gt;containers hierarchy&lt;/a&gt; in practice — a &lt;code&gt;university&lt;/code&gt; database, default &lt;code&gt;public&lt;/code&gt; schema, one table):&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot; &gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;CREATE DATABASE&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; university&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;-- connect to it: \c university   (or open a pgAdmin Query Tool on it)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;CREATE TABLE&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; students&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    id     &lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;integer      PRIMARY KEY&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    name   varchar&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;100&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; NOT NULL&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    course &lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;varchar&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;50&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;  NOT NULL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;INSERT INTO&lt;/span&gt;&lt;span&gt; students &lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;VALUES&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;#39;Asha&amp;#39;&lt;/span&gt;&lt;span&gt;,  &lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;#39;AI&amp;#39;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;#39;Ravi&amp;#39;&lt;/span&gt;&lt;span&gt;,  &lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;#39;AI&amp;#39;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;3&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;#39;Meena&amp;#39;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;#39;Data Science&amp;#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The same &lt;code&gt;PRIMARY KEY&lt;/code&gt; and &lt;code&gt;NOT NULL&lt;/code&gt; constraints from PostgreSQL 101 now guard your lab data — try inserting a duplicate &lt;code&gt;id&lt;/code&gt; and watch them work.&lt;/p&gt;
&lt;h2 id=&quot;one-new-dependency&quot;&gt;One new dependency&lt;/h2&gt;
&lt;p&gt;Python&#39;s standard library speaks HTTP but not PostgreSQL&#39;s wire protocol — remember from &lt;a href=&quot;/databases/postgresql-101/&quot;&gt;PostgreSQL 101&lt;/a&gt; that &lt;em&gt;every&lt;/em&gt; client talks to the engine over port 5432 in that protocol. The translator is a &lt;strong&gt;driver&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;pip&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; install &amp;quot;psycopg[binary]&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;psycopg&lt;/code&gt; is &lt;em&gt;exactly&lt;/em&gt; the &quot;your app / ORM&quot; box from the engine simulator: it opens the connection, sends your SQL down the wire, and hands rows back as Python tuples. That&#39;s the whole magic.&lt;/p&gt;
&lt;h2 id=&quot;the-server-now-database-backed&quot;&gt;The server, now database-backed&lt;/h2&gt;
&lt;p&gt;Save as &lt;code&gt;server.py&lt;/code&gt; — the diff from part 1 is small enough to read in one breath: &lt;code&gt;STUDENTS&lt;/code&gt; is gone, &lt;code&gt;fetch_students()&lt;/code&gt; is new, and &lt;code&gt;render()&lt;/code&gt; now escapes its values (the lesson part 1 warned about):&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot; &gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;# server.py — part 2: server-side rendering from PostgreSQL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;from&lt;/span&gt;&lt;span&gt; html&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; import&lt;/span&gt;&lt;span&gt; escape&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;from&lt;/span&gt;&lt;span&gt; http&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span&gt;server&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; import&lt;/span&gt;&lt;span&gt; HTTPServer&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span&gt; BaseHTTPRequestHandler&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;from&lt;/span&gt;&lt;span&gt; urllib&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; import&lt;/span&gt;&lt;span&gt; urlparse&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span&gt; parse_qs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;import&lt;/span&gt;&lt;span&gt; psycopg&lt;/span&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;  # pip install &amp;quot;psycopg[binary]&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;DSN&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;dbname=university user=postgres host=127.0.0.1&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;  # adjust user/password&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;PAGE&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;&amp;quot;&amp;quot;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Students&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;  &amp;lt;h1&amp;gt;Students — &lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;{course}&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;  &amp;lt;ul&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;{rows}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;  &amp;lt;/ul&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;lt;/html&amp;gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; fetch_students&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;course&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    query&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;SELECT id, name, course FROM students&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    params&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;/span&gt;&lt;span&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    if&lt;/span&gt;&lt;span&gt; course&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; !=&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;all&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        query&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; +=&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot; WHERE course = &lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;%s&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;     # %s = placeholder, NOT string formatting&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        params&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;append&lt;/span&gt;&lt;span&gt;(course)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    query&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; +=&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot; ORDER BY id&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    with&lt;/span&gt;&lt;span&gt; psycopg&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;connect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;DSN&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; as&lt;/span&gt;&lt;span&gt; conn&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;        with&lt;/span&gt;&lt;span&gt; conn&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;cursor&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; as&lt;/span&gt;&lt;span&gt; cur&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            cur&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;execute&lt;/span&gt;&lt;span&gt;(query&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span&gt; params)&lt;/span&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;    # the driver sends value separately&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;            return&lt;/span&gt;&lt;span&gt; cur&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;fetchall&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;         # → [(1, &amp;#39;Asha&amp;#39;, &amp;#39;AI&amp;#39;), ...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; render&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;course&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    rows&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;        f&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;    &amp;lt;li&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;escape&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;str&lt;/span&gt;&lt;span&gt;(sid))&lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; — &lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;escape&lt;/span&gt;&lt;span&gt;(name)&lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;escape&lt;/span&gt;&lt;span&gt;(c)&lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;)&amp;lt;/li&amp;gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;        for&lt;/span&gt;&lt;span&gt; sid&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span&gt; c&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; fetch_students&lt;/span&gt;&lt;span&gt;(course)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt; PAGE&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;format&lt;/span&gt;&lt;span&gt;(course&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;escape&lt;/span&gt;&lt;span&gt;(course)&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span&gt; rows&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;=&lt;/span&gt;&lt;span&gt;rows)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Handler&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;BaseHTTPRequestHandler&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; do_GET&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;self&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        url&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; urlparse&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span&gt;path)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;        if&lt;/span&gt;&lt;span&gt; url&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; !=&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;/students&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt;            self&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;send_error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;404&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;try /students&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;            return&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        course&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; parse_qs&lt;/span&gt;&lt;span&gt;(url&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span&gt;query)&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;course&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;all&amp;quot;&lt;/span&gt;&lt;span&gt;])[&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        body&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; render&lt;/span&gt;&lt;span&gt;(course)&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;encode&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt;        self&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;send_response&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;200&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt;        self&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;send_header&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;Content-Type&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;text/html; charset=utf-8&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt;        self&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;send_header&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;Content-Length&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; str&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #F07178;&quot;&gt;len&lt;/span&gt;&lt;span&gt;(body)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt;        self&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;end_headers&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt;        self&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span&gt;wfile&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;write&lt;/span&gt;&lt;span&gt;(body)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;if&lt;/span&gt;&lt;span&gt; __name__&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; ==&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F07178;&quot;&gt;    print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;serving on http://127.0.0.1:8000/students&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;    HTTPServer&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;127.0.0.1&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 8000&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span&gt; Handler)&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;serve_forever&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run &lt;code&gt;python3 server.py&lt;/code&gt; and open &lt;code&gt;http://127.0.0.1:8000/students&lt;/code&gt; — same page as part 1, except the rows just crossed a network protocol to get to you.&lt;/p&gt;
&lt;h2 id=&quot;the-journey-of-one-request&quot;&gt;The journey of one request&lt;/h2&gt;
&lt;p&gt;You&#39;ve now met every piece of this pipeline in a previous guide. Watch them work as one machine:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-db-render&gt;&lt;/sim-db-render&gt;&lt;/div&gt;
&lt;p&gt;Count the layers a single refresh touches: HTTP parsing (the servers guide), a SQL query over the wire, the engine&#39;s parser → planner → executor (PostgreSQL 101), rows back, the template merge (part 1), and an HTTP response. &lt;strong&gt;Two protocols, one page.&lt;/strong&gt; Every Django, Rails, and Spring request you&#39;ll ever debug is this exact journey wearing a bigger coat.&lt;/p&gt;
&lt;h2 id=&quot;two-injections-one-law&quot;&gt;Two injections, one law&lt;/h2&gt;
&lt;p&gt;Look closely at the two deliberate safety choices in the code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SQL&lt;/strong&gt;: the query uses &lt;code&gt;%s&lt;/code&gt; and passes &lt;code&gt;course&lt;/code&gt; as a &lt;em&gt;parameter&lt;/em&gt;. The driver ships the value separately from the SQL text, so a visitor requesting &lt;code&gt;?course=AI&#39;; DROP TABLE students;--&lt;/code&gt; sends a harmless &lt;em&gt;string&lt;/em&gt;, not a command. Never build SQL with f-strings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTML&lt;/strong&gt;: every value goes through &lt;code&gt;escape()&lt;/code&gt; before entering the page, so a student named &lt;code&gt;&amp;lt;script&amp;gt;…&amp;lt;/script&amp;gt;&lt;/code&gt; renders as text, not code — part 1&#39;s warning, now actually implemented.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Different layer, same law: &lt;strong&gt;data must never be allowed to become code.&lt;/strong&gt; SQL injection and XSS are both what happens when that law is broken. (OWASP&#39;s cheat sheets below are the professional references.)&lt;/p&gt;
&lt;h2 id=&quot;prove-it-s-live&quot;&gt;Prove it&#39;s live&lt;/h2&gt;
&lt;p&gt;The demo that separates part 2 from part 1 — while the server is running, in pgAdmin or psql:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot; &gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;INSERT INTO&lt;/span&gt;&lt;span&gt; students &lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;VALUES&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;4&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;#39;Tashi&amp;#39;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;#39;AI&amp;#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;UPDATE&lt;/span&gt;&lt;span&gt; students &lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;SET&lt;/span&gt;&lt;span&gt; course &lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;#39;Data Science&amp;#39;&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; WHERE name&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;#39;Ravi&amp;#39;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now &lt;strong&gt;just refresh the browser.&lt;/strong&gt; New student, moved student — no code edit, no restart. In part 1 that was impossible. The server doesn&#39;t &lt;em&gt;contain&lt;/em&gt; the page; it &lt;em&gt;manufactures&lt;/em&gt; it from current data, per request. That single sentence is what &quot;database-backed server-side rendering&quot; means.&lt;/p&gt;
&lt;h2 id=&quot;exercises&quot;&gt;Exercises&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Constraint in the wild&lt;/strong&gt;: &lt;code&gt;INSERT&lt;/code&gt; a student with a duplicate &lt;code&gt;id&lt;/code&gt; in pgAdmin, and confirm the error message matches the constraints simulator in PostgreSQL 101.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sort and count&lt;/strong&gt;: change the query to &lt;code&gt;ORDER BY name&lt;/code&gt;, then add a line under the &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; showing &lt;code&gt;len(...)&lt;/code&gt; students rendered.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A second route&lt;/strong&gt;: add &lt;code&gt;/courses&lt;/code&gt; that renders &lt;code&gt;SELECT DISTINCT course FROM students&lt;/code&gt; as a list of links to &lt;code&gt;/students?course=...&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Attack yourself, then fix it&lt;/strong&gt;: make a copy where &lt;code&gt;fetch_students&lt;/code&gt; builds the query with an f-string instead of &lt;code&gt;%s&lt;/code&gt;, request &lt;code&gt;?course=AI&#39; OR &#39;1&#39;=&#39;1&lt;/code&gt;, and watch the filter break (every student leaks). Restore the parameterized version and confirm the same URL is now harmless data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feel the connection cost&lt;/strong&gt;: our code connects to PostgreSQL on &lt;em&gt;every&lt;/em&gt; request. Move &lt;code&gt;psycopg.connect&lt;/code&gt; to module level and reload rapidly — snappier? That&#39;s why real servers use connection &lt;em&gt;pools&lt;/em&gt; (and what breaks with threads — recall exercise 4 of part 1).&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;OWASP Foundation. (n.d.). &lt;em&gt;Cross site scripting prevention cheat sheet&lt;/em&gt;. Retrieved August 16, 2026, from https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html&lt;/li&gt;
&lt;li&gt;OWASP Foundation. (n.d.). &lt;em&gt;SQL injection prevention cheat sheet&lt;/em&gt;. Retrieved August 16, 2026, from https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html&lt;/li&gt;
&lt;li&gt;The PostgreSQL Global Development Group. (n.d.). &lt;em&gt;Queries&lt;/em&gt; (PostgreSQL documentation, Chapter 7). Retrieved August 16, 2026, from https://www.postgresql.org/docs/current/queries.html&lt;/li&gt;
&lt;li&gt;The Psycopg Team. (n.d.). &lt;em&gt;Psycopg 3 documentation&lt;/em&gt;. Retrieved August 16, 2026, from https://www.psycopg.org/psycopg3/docs/&lt;/li&gt;
&lt;li&gt;Python Software Foundation. (n.d.). &lt;em&gt;http.server — HTTP servers&lt;/em&gt; (Python documentation). Retrieved August 16, 2026, from https://docs.python.org/3/library/http.server.html&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Server-side rendering in pure Python, part 1: from data to HTML</title>
        <published>2026-08-16T00:00:00+00:00</published>
        <updated>2026-08-16T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://learn.kevalabs.com/python/server-side-rendering-in-pure-python/"/>
        <id>https://learn.kevalabs.com/python/server-side-rendering-in-pure-python/</id>
        
        <content type="html" xml:base="https://learn.kevalabs.com/python/server-side-rendering-in-pure-python/">&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt; Python 3 installed, plus the ideas from &lt;a href=&quot;/web-fundamentals/how-http-servers-work/&quot;&gt;How HTTP servers work&lt;/a&gt; and &lt;a href=&quot;/web-fundamentals/server-side-vs-client-side-rendering/&quot;&gt;SSR vs CSR&lt;/a&gt;. Time: ~30 minutes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Server-side rendering sounds like a framework feature. It isn&#39;t — it&#39;s just &lt;em&gt;building a string of HTML from data, on the server, per request&lt;/em&gt;. Today you&#39;ll do it with nothing but Python&#39;s standard library, so there&#39;s nowhere for the magic to hide.&lt;/p&gt;
&lt;h2 id=&quot;what-rendering-literally-is&quot;&gt;What &quot;rendering&quot; literally is&lt;/h2&gt;
&lt;p&gt;Before the server, understand the core move: a &lt;strong&gt;template&lt;/strong&gt; with holes, &lt;strong&gt;data&lt;/strong&gt;, and a merge:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-template-merge&gt;&lt;/sim-template-merge&gt;&lt;/div&gt;
&lt;p&gt;That&#39;s the entire trick. Real template engines (Django&#39;s, Jinja2) add loops, inheritance, and — critically — &lt;em&gt;escaping&lt;/em&gt; (more on that at the end), but the mental model stays: template + data → HTML string.&lt;/p&gt;
&lt;h2 id=&quot;the-server-complete&quot;&gt;The server, complete&lt;/h2&gt;
&lt;p&gt;Save this as &lt;code&gt;server.py&lt;/code&gt; — it&#39;s the whole lab, ~45 lines, standard library only:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot; &gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;# server.py — server-side rendering with only the standard library&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;from&lt;/span&gt;&lt;span&gt; http&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span&gt;server&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; import&lt;/span&gt;&lt;span&gt; HTTPServer&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span&gt; BaseHTTPRequestHandler&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;from&lt;/span&gt;&lt;span&gt; urllib&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; import&lt;/span&gt;&lt;span&gt; urlparse&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span&gt; parse_qs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;STUDENTS&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;name&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;Asha&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;  &amp;quot;course&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;AI&amp;quot;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;name&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;Ravi&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;  &amp;quot;course&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;AI&amp;quot;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 3&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;name&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;Meena&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;course&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;Data Science&amp;quot;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;PAGE&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;&amp;quot;&amp;quot;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Students&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;  &amp;lt;h1&amp;gt;Students — &lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;{course}&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;  &amp;lt;ul&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;{rows}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;  &amp;lt;/ul&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;lt;/html&amp;gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; render&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;course&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    wanted&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;/span&gt;&lt;span&gt; [s&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; for&lt;/span&gt;&lt;span&gt; s&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt; STUDENTS&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; if&lt;/span&gt;&lt;span&gt; course&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; in&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;all&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span&gt; s[&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;course&amp;quot;&lt;/span&gt;&lt;span&gt;])]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    rows&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;        f&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;#39;    &amp;lt;li&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;{&lt;/span&gt;&lt;span&gt;s[&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; — &lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;{&lt;/span&gt;&lt;span&gt;s[&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;{&lt;/span&gt;&lt;span&gt;s[&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;course&amp;quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;)&amp;lt;/li&amp;gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; for&lt;/span&gt;&lt;span&gt; s&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; in&lt;/span&gt;&lt;span&gt; wanted&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt; PAGE&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;format&lt;/span&gt;&lt;span&gt;(course&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;=&lt;/span&gt;&lt;span&gt;course&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span&gt; rows&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;=&lt;/span&gt;&lt;span&gt;rows)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Handler&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;BaseHTTPRequestHandler&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; do_GET&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;self&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        url&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; urlparse&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span&gt;path)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;        if&lt;/span&gt;&lt;span&gt; url&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; !=&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;/students&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt;            self&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;send_error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;404&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;try /students&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;            return&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        course&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; parse_qs&lt;/span&gt;&lt;span&gt;(url&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span&gt;query)&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;course&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;all&amp;quot;&lt;/span&gt;&lt;span&gt;])[&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        body&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; render&lt;/span&gt;&lt;span&gt;(course)&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;encode&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt;        self&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;send_response&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;200&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt;        self&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;send_header&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;Content-Type&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;text/html; charset=utf-8&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt;        self&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;send_header&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;Content-Length&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; str&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #F07178;&quot;&gt;len&lt;/span&gt;&lt;span&gt;(body)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt;        self&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;end_headers&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt;        self&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span&gt;wfile&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;write&lt;/span&gt;&lt;span&gt;(body)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;if&lt;/span&gt;&lt;span&gt; __name__&lt;/span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; ==&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F07178;&quot;&gt;    print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;serving on http://127.0.0.1:8000/students&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;    HTTPServer&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;127.0.0.1&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 8000&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;/span&gt;&lt;span&gt; Handler)&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;serve_forever&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run it and visit the page:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;python3&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; server.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;# → http://127.0.0.1:8000/students&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;read-it-as-the-server-loop&quot;&gt;Read it as the server loop&lt;/h2&gt;
&lt;p&gt;Map the code onto the loop from &lt;a href=&quot;/web-fundamentals/how-http-servers-work/&quot;&gt;How HTTP servers work&lt;/a&gt; — every stage is here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;accept + parse&lt;/strong&gt; — &lt;code&gt;http.server&lt;/code&gt; does both and calls &lt;code&gt;do_GET&lt;/code&gt; with &lt;code&gt;self.path&lt;/code&gt; filled in.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;route&lt;/strong&gt; — our humble &lt;code&gt;if url.path != &quot;/students&quot;&lt;/code&gt; &lt;em&gt;is&lt;/em&gt; the routing table (with &lt;code&gt;send_error(404)&lt;/code&gt; as the fall-through).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;handler&lt;/strong&gt; — &lt;code&gt;render(course)&lt;/code&gt;: filter the data, build rows, fill the template. This is the &lt;em&gt;dynamic&lt;/em&gt; lane from the static-vs-dynamic simulator, and the SSR lane from the race.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;respond&lt;/strong&gt; — status, &lt;code&gt;Content-Type&lt;/code&gt;, &lt;code&gt;Content-Length&lt;/code&gt;, body. The exact anatomy from the HTTP guide, typed by hand.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Notice &lt;code&gt;?course=AI&lt;/code&gt; — visit &lt;code&gt;http://127.0.0.1:8000/students?course=AI&lt;/code&gt; and the server renders a &lt;em&gt;different page from the same template&lt;/em&gt;. That per-request variation is what makes it dynamic.&lt;/p&gt;
&lt;h2 id=&quot;prove-it-s-server-side&quot;&gt;Prove it&#39;s server-side&lt;/h2&gt;
&lt;p&gt;Three experiments, in order of persuasiveness:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;View-source&lt;/strong&gt; (&lt;code&gt;Ctrl+U&lt;/code&gt; / &lt;code&gt;Cmd+Option+U&lt;/code&gt;): Asha and Ravi are right there in the HTML. The browser received finished content.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Disable JavaScript&lt;/strong&gt; in DevTools and reload: the page is identical. There is no JS to depend on.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;curl&lt;/code&gt; it&lt;/strong&gt; — a program with no rendering engine at all sees the full content:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;http://127.0.0.1:8000/students?course=AI&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now do experiment 1 on a CSR app (any big dashboard-style site): the source shows a nearly-empty &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;. You can &lt;em&gt;see&lt;/em&gt; the strategy difference you learned in &lt;a href=&quot;/web-fundamentals/server-side-vs-client-side-rendering/&quot;&gt;SSR vs CSR&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;one-warning-before-you-go-escaping&quot;&gt;One warning before you go: escaping&lt;/h2&gt;
&lt;p&gt;Our &lt;code&gt;render()&lt;/code&gt; pastes data into HTML &lt;em&gt;raw&lt;/em&gt;. If a student&#39;s name were &lt;code&gt;&amp;lt;script&amp;gt;alert(1)&amp;lt;/script&amp;gt;&lt;/code&gt;, we&#39;d be injecting code into every visitor&#39;s browser — the classic &lt;strong&gt;XSS&lt;/strong&gt; vulnerability. The stdlib fix is one function: pass every value through &lt;code&gt;html.escape()&lt;/code&gt; from the &lt;code&gt;html&lt;/code&gt; module. Real template engines escape &lt;em&gt;by default&lt;/em&gt;, and that — not convenience — is the serious reason to use one in production.&lt;/p&gt;
&lt;h2 id=&quot;exercises&quot;&gt;Exercises&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Add a column&lt;/strong&gt;: give each student a &lt;code&gt;year&lt;/code&gt;, show it in the row.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add a route&lt;/strong&gt;: &lt;code&gt;/courses&lt;/code&gt; should render the list of distinct courses (hint: another template string + a second &lt;code&gt;if&lt;/code&gt; in &lt;code&gt;do_GET&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Escape it&lt;/strong&gt;: import &lt;code&gt;html&lt;/code&gt; and wrap every interpolated value in &lt;code&gt;html.escape()&lt;/code&gt;. Then add a student named &lt;code&gt;&amp;lt;b&amp;gt;Bold&amp;lt;/b&amp;gt;&lt;/code&gt; and confirm it displays as text instead of becoming markup.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Break the loop&lt;/strong&gt; (from the servers guide): add &lt;code&gt;time.sleep(5)&lt;/code&gt; inside &lt;code&gt;render()&lt;/code&gt; and open the page in two tabs at once. Which tab suffers, and why? (&lt;code&gt;HTTPServer&lt;/code&gt; is single-threaded; try &lt;code&gt;ThreadingHTTPServer&lt;/code&gt; and watch the difference.)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;what-s-next&quot;&gt;What&#39;s next&lt;/h2&gt;
&lt;p&gt;One honest limitation remains: &lt;code&gt;STUDENTS&lt;/code&gt; is a list frozen into the source code — changing the data means editing the program and restarting it. Real applications render from a database. That&#39;s exactly &lt;a href=&quot;/python/server-side-rendering-in-pure-python-part-2/&quot;&gt;part 2&lt;/a&gt;: same server, same template, but the rows come live from PostgreSQL.&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;MDN Web Docs. (n.d.). &lt;em&gt;Introduction to the server side&lt;/em&gt;. Mozilla. Retrieved August 16, 2026, from https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Server-side/First_steps/Introduction&lt;/li&gt;
&lt;li&gt;OWASP Foundation. (n.d.). &lt;em&gt;Cross site scripting prevention cheat sheet&lt;/em&gt;. Retrieved August 16, 2026, from https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html&lt;/li&gt;
&lt;li&gt;Python Software Foundation. (n.d.). &lt;em&gt;http.server — HTTP servers&lt;/em&gt; (Python documentation). Retrieved August 16, 2026, from https://docs.python.org/3/library/http.server.html&lt;/li&gt;
&lt;li&gt;Python Software Foundation. (n.d.). &lt;em&gt;html — HyperText Markup Language support&lt;/em&gt; (Python documentation). Retrieved August 16, 2026, from https://docs.python.org/3/library/html.html&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>How auth works: authentication vs authorization</title>
        <published>2026-08-16T00:00:00+00:00</published>
        <updated>2026-08-16T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://learn.kevalabs.com/security/authentication-vs-authorization/"/>
        <id>https://learn.kevalabs.com/security/authentication-vs-authorization/</id>
        
        <content type="html" xml:base="https://learn.kevalabs.com/security/authentication-vs-authorization/">&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt; &lt;a href=&quot;/web-fundamentals/how-http-works/&quot;&gt;How HTTP works&lt;/a&gt; — especially the cookies/sessions and status-code sections; this guide builds directly on both.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Two words that get mixed up constantly, and one distinction that untangles them:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Authentication&lt;/strong&gt; answers &lt;em&gt;&quot;who are you?&quot;&lt;/em&gt; — the login box, the password check, the session cookie.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorization&lt;/strong&gt; answers &lt;em&gt;&quot;what may you do?&quot;&lt;/em&gt; — and it&#39;s asked again on &lt;em&gt;every single request&lt;/em&gt;, long after login.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;They fail differently too: fail authentication and you get &lt;strong&gt;401 Unauthorized&lt;/strong&gt; (&quot;prove your identity&quot;); fail authorization and you get &lt;strong&gt;403 Forbidden&lt;/strong&gt; (&quot;we know exactly who you are — no&quot;). If you&#39;ve read &lt;a href=&quot;/web-fundamentals/how-http-works/&quot;&gt;How HTTP works&lt;/a&gt;, you already have all the machinery: this whole topic is status codes plus cookies.&lt;/p&gt;
&lt;h2 id=&quot;authentication-proving-who-you-are&quot;&gt;Authentication: proving who you are&lt;/h2&gt;
&lt;p&gt;HTTP is stateless — the server forgets you between requests. So authentication has two jobs: check your credentials &lt;em&gt;once&lt;/em&gt;, then give you something to carry that proves the check happened. That something is a session cookie. Watch a full login:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-login-flow&gt;&lt;/sim-login-flow&gt;&lt;/div&gt;
&lt;p&gt;Three details that matter in real systems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The server never stores your password.&lt;/strong&gt; It stores a &lt;em&gt;hash&lt;/em&gt; — a one-way fingerprint. At login it hashes what you typed and compares fingerprints. A database leak then exposes fingerprints, not passwords. (Every serious framework does this by default.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The session cookie is your badge, not your identity.&lt;/strong&gt; The server keeps a table mapping &lt;code&gt;sessionid → user&lt;/code&gt;; the cookie is just the claim ticket. That&#39;s why &quot;log out everywhere&quot; works — the server deletes its side of the mapping and every badge goes dead.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Browser apps redirect to &lt;code&gt;/login&lt;/code&gt; (302); APIs answer &lt;code&gt;401&lt;/code&gt;.&lt;/strong&gt; Same meaning, different audience: humans get a form, programs get a status code.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;authorization-what-you-may-do&quot;&gt;Authorization: what you may do&lt;/h2&gt;
&lt;p&gt;Being logged in is not permission. After authentication, &lt;em&gt;every request&lt;/em&gt; still passes a second gate: does this user have the right to do this thing? The standard way to manage that is &lt;strong&gt;roles&lt;/strong&gt; — bundle permissions into named jobs, assign users to roles:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-permissions&gt;&lt;/sim-permissions&gt;&lt;/div&gt;
&lt;p&gt;Note what the simulator shows: the professor can delete the course but &lt;em&gt;can&#39;t submit an assignment&lt;/em&gt; — authorization isn&#39;t a ladder where higher roles do everything, it&#39;s a matrix of who-does-what. And the check lives on the &lt;strong&gt;server&lt;/strong&gt;: hiding a button in the UI is cosmetics, not security. A student who crafts a raw &lt;code&gt;DELETE /course/42&lt;/code&gt; request must still hit a 403.&lt;/p&gt;
&lt;h2 id=&quot;401-vs-403-the-debugging-rule&quot;&gt;401 vs 403: the debugging rule&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;401 Unauthorized&lt;/strong&gt; → the server doesn&#39;t know who you are (or your session expired). Fix: authenticate. Misnamed by history — it really means &lt;em&gt;unauthenticated&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;403 Forbidden&lt;/strong&gt; → the server knows who you are and the answer is no. Fix: get the permission, or stop asking.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When an app misbehaves, this pair tells you instantly which half of auth to debug.&lt;/p&gt;
&lt;h2 id=&quot;every-framework-ships-this-your-job-is-the-matrix&quot;&gt;Every framework ships this — your job is the matrix&lt;/h2&gt;
&lt;p&gt;None of the above is exotic: every serious web stack implements the same two gates, whatever the language. Stripped of syntax, the server-side logic is always:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;on every request:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    user = session_table[ request.cookie(&amp;quot;sessionid&amp;quot;) ]     # authentication&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    if user is missing:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        return 401  (or redirect to /login)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    if not permitted(user.role, request.action):            # authorization&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        return 403&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    handle the request&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Django calls it &lt;code&gt;contrib.auth&lt;/code&gt;, Spring calls it Spring Security, Rails has Devise, Laravel has guards and policies — same machinery, different names. Two consequences:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Don&#39;t hand-roll it.&lt;/strong&gt; Password hashing, session management, and permission checks are exactly where home-made code gets breached. Use what your framework provides.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The part only &lt;em&gt;you&lt;/em&gt; can do is design the permission matrix&lt;/strong&gt; — the roles × actions table from the simulator, for your application&#39;s real actions. Sketch it before you code; it&#39;s a design document, not an afterthought.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;takeaways&quot;&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Two questions, two gates&lt;/strong&gt;: authentication once at login (&quot;who are you?&quot;), authorization on every request (&quot;may you do this?&quot;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;401 = prove identity; 403 = identity proven, still no.&lt;/strong&gt; Memorize the pair — it&#39;s your fastest debugging signal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Passwords are stored as hashes&lt;/strong&gt;, never plaintext. If a tutorial stores plaintext, close the tab.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The session cookie is a claim ticket&lt;/strong&gt;, meaningful only against the server&#39;s session table.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorization lives on the server.&lt;/strong&gt; Hidden buttons are UX; the 403 is the security.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use your framework&#39;s auth machinery&lt;/strong&gt; — whatever the stack — and spend your effort designing the permission matrix instead.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Barth, A. (2011). &lt;em&gt;HTTP state management mechanism&lt;/em&gt; (RFC 6265). Internet Engineering Task Force. https://doi.org/10.17487/RFC6265&lt;/li&gt;
&lt;li&gt;Fielding, R., Nottingham, M., &amp;amp; Reschke, J. (2022). &lt;em&gt;HTTP semantics&lt;/em&gt; (RFC 9110). Internet Engineering Task Force. https://doi.org/10.17487/RFC9110&lt;/li&gt;
&lt;li&gt;MDN Web Docs. (n.d.). &lt;em&gt;HTTP authentication&lt;/em&gt;. Mozilla. Retrieved August 16, 2026, from https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Authentication&lt;/li&gt;
&lt;li&gt;OWASP Foundation. (n.d.). &lt;em&gt;Authentication cheat sheet&lt;/em&gt;. Retrieved August 16, 2026, from https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html&lt;/li&gt;
&lt;li&gt;OWASP Foundation. (n.d.). &lt;em&gt;Authorization cheat sheet&lt;/em&gt;. Retrieved August 16, 2026, from https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>How browsers work: from URL to pixels</title>
        <published>2026-08-16T00:00:00+00:00</published>
        <updated>2026-08-16T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://learn.kevalabs.com/web-fundamentals/how-browsers-work/"/>
        <id>https://learn.kevalabs.com/web-fundamentals/how-browsers-work/</id>
        
        <content type="html" xml:base="https://learn.kevalabs.com/web-fundamentals/how-browsers-work/">&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt; &lt;a href=&quot;/web-fundamentals/how-http-works/&quot;&gt;How HTTP works&lt;/a&gt; — this guide assumes you know what requests, responses, and round trips are.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Fast sites feel fast because their developers understand two hard facts about the web:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Latency is the enemy.&lt;/strong&gt; Every byte travels a physical distance, and the protocols underneath HTTP demand several round trips before the first byte of HTML even arrives.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The browser is (mostly) single-threaded.&lt;/strong&gt; One main thread parses your HTML, runs your JavaScript, computes layout, and responds to the user&#39;s taps. Whatever you make it do, it can&#39;t do anything else at the same time.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Everything a browser does between &quot;Enter&quot; and &quot;pixels&quot; is shaped by those two constraints. Let&#39;s walk the whole pipeline, step by step. Each step comes with a simulator — &lt;strong&gt;press play or step through them&lt;/strong&gt;; they&#39;re the point of this guide.&lt;/p&gt;
&lt;h2 id=&quot;step-1-navigation-finding-the-server&quot;&gt;Step 1 — Navigation: finding the server&lt;/h2&gt;
&lt;h3 id=&quot;dns-turning-a-name-into-an-address&quot;&gt;DNS: turning a name into an address&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;learn.kevalabs.com&lt;/code&gt; means nothing to a network router. Before anything can be requested, the browser needs an IP address, and it gets one through DNS — a hierarchy of caches and servers, asked in order from cheapest to most authoritative.&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-dns-lookup&gt;&lt;/sim-dns-lookup&gt;&lt;/div&gt;
&lt;p&gt;The important performance detail: every hop in that chain &lt;strong&gt;caches&lt;/strong&gt; the answer. The first visitor pays the full trip; subsequent lookups are answered in microseconds from a cache nearby. But every &lt;em&gt;unique hostname&lt;/em&gt; on your page — fonts CDN, analytics, image host — pays its own lookup, which is why piling up third-party domains hurts, especially on mobile networks where each lookup can take hundreds of milliseconds.&lt;/p&gt;
&lt;h3 id=&quot;tcp-and-tls-opening-a-trustworthy-pipe&quot;&gt;TCP and TLS: opening a trustworthy pipe&lt;/h3&gt;
&lt;p&gt;With an IP in hand, the browser can&#39;t just start sending HTML requests. First it must establish a TCP connection (the three-way handshake), and for HTTPS, negotiate TLS encryption on top of it. Watch how many times messages cross the wire before a single byte of your page moves:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-handshake&gt;&lt;/sim-handshake&gt;&lt;/div&gt;
&lt;p&gt;Three round trips before the first byte of HTML. If the server is 100 ms away, that&#39;s 300 ms of pure protocol overhead — nothing downloaded yet. This is why HTTPS session resumption, HTTP/2 connection reuse, and QUIC/HTTP-3 (which merges the transport and crypto handshakes) exist: the handshakes are the tax, and modern protocols work hard to pay it once.&lt;/p&gt;
&lt;h2 id=&quot;step-2-the-response-and-the-14-kb-rule&quot;&gt;Step 2 — The response and the 14 KB rule&lt;/h2&gt;
&lt;p&gt;The request is out; the server responds with the first byte of HTML (&lt;strong&gt;Time To First Byte&lt;/strong&gt;, TTFB). But the server can&#39;t firehose the whole page at once. TCP starts cautiously — a mechanism called &lt;strong&gt;slow start&lt;/strong&gt; — sending a small initial window (~10 packets, about 14 KB), then doubling it after each acknowledged round trip:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-slow-start&gt;&lt;/sim-slow-start&gt;&lt;/div&gt;
&lt;p&gt;That first ~14 KB window is why performance guides obsess over what&#39;s in the top of your HTML: if enough of the document (and its critical CSS) fits in the first window, the browser can start working after a &lt;em&gt;single&lt;/em&gt; round trip. And congestion control cuts both ways — when a packet is lost, the window shrinks and rebuilds, which is the transport layer telling you that every kilobyte of critical path matters.&lt;/p&gt;
&lt;h2 id=&quot;step-3-parsing-from-bytes-to-trees&quot;&gt;Step 3 — Parsing: from bytes to trees&lt;/h2&gt;
&lt;h3 id=&quot;building-the-dom&quot;&gt;Building the DOM&lt;/h3&gt;
&lt;p&gt;As HTML bytes stream in, the parser tokenizes them and builds the &lt;strong&gt;DOM tree&lt;/strong&gt; — the object model of your document. Two things make this step interesting: some resources &lt;em&gt;block&lt;/em&gt; the parser, and the browser cheats around that with a second, lightweight scanner. Here&#39;s the document the simulator parses:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot; &gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;doctype&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; html&lt;/span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;head&lt;/span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;link&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; rel&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;style.css&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;head&lt;/span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;Hi&lt;/span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;img&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; src&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;cat.png&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; src&lt;/span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;app.js&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;Bye&lt;/span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-dom-parser&gt;&lt;/sim-dom-parser&gt;&lt;/div&gt;
&lt;p&gt;Two takeaways from that run:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; without &lt;code&gt;async&lt;/code&gt; or &lt;code&gt;defer&lt;/code&gt; &lt;strong&gt;halts the parser&lt;/strong&gt; — the script might &lt;code&gt;document.write()&lt;/code&gt;, so the browser can&#39;t safely continue until it&#39;s downloaded &lt;em&gt;and&lt;/em&gt; executed.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;preload scanner&lt;/strong&gt; softens the blow: it races ahead of the blocked parser, spotting &lt;code&gt;img&lt;/code&gt;, &lt;code&gt;link&lt;/code&gt;, and &lt;code&gt;script&lt;/code&gt; URLs and requesting them early, so the downloads are usually done by the time the parser catches up.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;building-the-cssom&quot;&gt;Building the CSSOM&lt;/h3&gt;
&lt;p&gt;CSS gets its own tree: the &lt;strong&gt;CSSOM&lt;/strong&gt;. The browser parses every rule, then resolves what each node&#39;s final, computed style is — walking down the tree so that inheritance and the cascade (specificity, then source order) fall out naturally:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-cssom&gt;&lt;/sim-cssom&gt;&lt;/div&gt;
&lt;p&gt;Building the CSSOM is fast, but it&#39;s &lt;strong&gt;render-blocking&lt;/strong&gt;: the browser refuses to paint anything until it knows the final styles — a half-styled flash would be worse. Keep the CSS on the critical path small.&lt;/p&gt;
&lt;p&gt;Meanwhile, off the main thread, JavaScript files are compiled, and the browser also builds an &lt;strong&gt;accessibility tree&lt;/strong&gt; — the structure screen readers and other assistive tech actually consume.&lt;/p&gt;
&lt;h2 id=&quot;step-4-render-style-layout-paint-composite&quot;&gt;Step 4 — Render: style → layout → paint → composite&lt;/h2&gt;
&lt;h3 id=&quot;the-render-tree&quot;&gt;The render tree&lt;/h3&gt;
&lt;p&gt;DOM and CSSOM combine into the &lt;strong&gt;render tree&lt;/strong&gt;: only the nodes that will actually produce pixels. Non-visual nodes (&lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt;) are dropped, and so is anything with &lt;code&gt;display: none&lt;/code&gt; — but &lt;code&gt;visibility: hidden&lt;/code&gt; stays, because an invisible box still occupies space:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-render-tree&gt;&lt;/sim-render-tree&gt;&lt;/div&gt;
&lt;h3 id=&quot;layout-where-and-how-big&quot;&gt;Layout: where and how big?&lt;/h3&gt;
&lt;p&gt;With the render tree ready, the browser runs &lt;strong&gt;layout&lt;/strong&gt; (also called &lt;em&gt;reflow&lt;/em&gt; when it happens again): starting from the viewport width, it computes the exact position and size of every box. Resize the viewport in the simulator and watch every box get recomputed:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-layout&gt;&lt;/sim-layout&gt;&lt;/div&gt;
&lt;p&gt;Layout is recursive and expensive — changing one element&#39;s width can dirty everything below it. This is why images without declared dimensions are a classic sin: each one that arrives forces another reflow (and a layout shift the user can &lt;em&gt;see&lt;/em&gt;).&lt;/p&gt;
&lt;h3 id=&quot;paint-and-composite&quot;&gt;Paint and composite&lt;/h3&gt;
&lt;p&gt;Finally the browser &lt;strong&gt;paints&lt;/strong&gt; — rasterizing each render-tree node into pixels (text, colors, borders, shadows, in a defined order) — and &lt;strong&gt;composites&lt;/strong&gt;: layers get stitched together on the GPU. Some elements (transforms, &lt;code&gt;will-change&lt;/code&gt;, videos, canvas) are &lt;em&gt;promoted&lt;/em&gt; to their own compositor layer, and that has a huge consequence for animation:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-pipeline&gt;&lt;/sim-pipeline&gt;&lt;/div&gt;
&lt;p&gt;Animate &lt;code&gt;left&lt;/code&gt; and every frame re-runs style → layout → paint → composite on the main thread. Animate &lt;code&gt;transform&lt;/code&gt; and the compositor slides an already-painted layer around — off the main thread, silky even while your JS is busy. This one habit separates janky UIs from smooth ones.&lt;/p&gt;
&lt;h2 id=&quot;step-5-interactivity-the-main-thread-is-a-queue&quot;&gt;Step 5 — Interactivity: the main thread is a queue&lt;/h2&gt;
&lt;p&gt;The page &lt;em&gt;looks&lt;/em&gt; ready — but &quot;ready&quot; only counts if it responds when tapped. Rendering work and your JavaScript share one main thread, and a long task blocks everything, including click handlers:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-main-thread&gt;&lt;/sim-main-thread&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Time to Interactive&lt;/strong&gt; is the moment the main thread is free enough to respond within ~50 ms. A page that paints in one second but ships a 2-second JavaScript boot task &lt;em&gt;feels&lt;/em&gt; broken: clicks land in a queue and wait. Break long tasks up, defer what isn&#39;t critical, and move real computation to Web Workers.&lt;/p&gt;
&lt;h2 id=&quot;takeaways&quot;&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Round trips dominate first-visit latency&lt;/strong&gt;: DNS + TCP + TLS cost ~3 round trips before any HTML moves. Fewer unique hostnames, reused connections, and modern protocols (HTTP/2, HTTP/3) are how you pay less.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The first 14 KB is special&lt;/strong&gt; — TCP slow start delivers it after one round trip. Fit your critical HTML/CSS in it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CSS blocks rendering; sync scripts block parsing.&lt;/strong&gt; Keep critical CSS lean; use &lt;code&gt;defer&lt;/code&gt;/&lt;code&gt;async&lt;/code&gt; everywhere you can. The preload scanner helps, but don&#39;t hide resources from it (e.g. behind JS-injected tags).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Layout is expensive and cascading.&lt;/strong&gt; Declare image dimensions; batch DOM reads/writes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Animate with &lt;code&gt;transform&lt;/code&gt; and &lt;code&gt;opacity&lt;/code&gt;&lt;/strong&gt;, not &lt;code&gt;left&lt;/code&gt;/&lt;code&gt;top&lt;/code&gt;/&lt;code&gt;width&lt;/code&gt; — compositor-only animations skip layout and paint entirely.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The main thread is the whole game for interactivity.&lt;/strong&gt; Under ~50 ms per task, or the user feels it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Allman, M., Paxson, V., &amp;amp; Blanton, E. (2009). &lt;em&gt;TCP congestion control&lt;/em&gt; (RFC 5681). Internet Engineering Task Force. https://doi.org/10.17487/RFC5681&lt;/li&gt;
&lt;li&gt;Grigorik, I. (2013). &lt;em&gt;High performance browser networking&lt;/em&gt;. O&#39;Reilly Media. https://hpbn.co/&lt;/li&gt;
&lt;li&gt;Kosaka, M. (2018). &lt;em&gt;Inside look at modern web browser (part 3)&lt;/em&gt;. Chrome for Developers. https://developer.chrome.com/blog/inside-browser-part3&lt;/li&gt;
&lt;li&gt;MDN Web Docs. (n.d.). &lt;em&gt;Critical rendering path&lt;/em&gt;. Mozilla. Retrieved August 16, 2026, from https://developer.mozilla.org/en-US/docs/Web/Performance/Guides/Critical_rendering_path&lt;/li&gt;
&lt;li&gt;MDN Web Docs. (n.d.). &lt;em&gt;How browsers work&lt;/em&gt;. Mozilla. Retrieved August 16, 2026, from https://developer.mozilla.org/en-US/docs/Web/Performance/Guides/How_browsers_work&lt;/li&gt;
&lt;li&gt;Mockapetris, P. (1987). &lt;em&gt;Domain names—Concepts and facilities&lt;/em&gt; (RFC 1034). Internet Engineering Task Force. https://doi.org/10.17487/RFC1034&lt;/li&gt;
&lt;li&gt;Rescorla, E. (2018). &lt;em&gt;The Transport Layer Security (TLS) protocol version 1.3&lt;/em&gt; (RFC 8446). Internet Engineering Task Force. https://doi.org/10.17487/RFC8446&lt;/li&gt;
&lt;li&gt;WHATWG. (2026). &lt;em&gt;HTML living standard: Parsing HTML documents&lt;/em&gt;. https://html.spec.whatwg.org/multipage/parsing.html&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Structure follows MDN&#39;s &lt;a rel=&quot;external&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/Performance/Guides/How_browsers_work&quot;&gt;How browsers work&lt;/a&gt; (CC-BY-SA); the prose, mistakes, and simulators are ours.&lt;/em&gt;&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>How HTTP servers work: the loop behind every response</title>
        <published>2026-08-16T00:00:00+00:00</published>
        <updated>2026-08-16T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://learn.kevalabs.com/web-fundamentals/how-http-servers-work/"/>
        <id>https://learn.kevalabs.com/web-fundamentals/how-http-servers-work/</id>
        
        <content type="html" xml:base="https://learn.kevalabs.com/web-fundamentals/how-http-servers-work/">&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt; &lt;a href=&quot;/web-fundamentals/how-http-works/&quot;&gt;How HTTP works&lt;/a&gt; — you&#39;ll need requests, responses, and status codes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Strip away the mystique and an HTTP server is a surprisingly small idea: &lt;strong&gt;a program that never exits, listening on a port, answering one question over and over&lt;/strong&gt; — &lt;em&gt;&quot;here&#39;s a request; what&#39;s the response?&quot;&lt;/em&gt; Everything else (frameworks, routing, middleware, nginx) is engineering layered on that loop.&lt;/p&gt;
&lt;p&gt;If you know &lt;a href=&quot;/web-fundamentals/how-http-works/&quot;&gt;how HTTP messages look on the wire&lt;/a&gt;, you already know the server&#39;s input and output. This guide is about what happens in between.&lt;/p&gt;
&lt;h2 id=&quot;the-loop-accept-parse-route-respond&quot;&gt;The loop: accept → parse → route → respond&lt;/h2&gt;
&lt;p&gt;When you run a server on port 8000, the operating system starts delivering TCP connections to your program. For each one, the server reads the raw request text, parses out the method and path, looks up which piece of code should handle that path, runs it, and writes the response back:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-server-loop&gt;&lt;/sim-server-loop&gt;&lt;/div&gt;
&lt;p&gt;Two things to notice. First, &lt;strong&gt;the router is just a lookup table&lt;/strong&gt; — path patterns on one side, functions on the other, and a fall-through to 404 when nothing matches. Second, the 404 in the simulator isn&#39;t an error in the server — it&#39;s the server &lt;em&gt;working correctly&lt;/em&gt;, telling the client its request matched nothing. (That&#39;s the 4xx = client&#39;s-side rule from the HTTP guide.)&lt;/p&gt;
&lt;h2 id=&quot;static-vs-dynamic-read-a-file-or-run-code&quot;&gt;Static vs dynamic: read a file, or run code&lt;/h2&gt;
&lt;p&gt;Every response a server produces is one of two kinds, and the difference drives most of web architecture:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-static-dynamic&gt;&lt;/sim-static-dynamic&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Static&lt;/strong&gt; — the response already exists as a file (&lt;code&gt;logo.png&lt;/code&gt;, &lt;code&gt;style.css&lt;/code&gt;, this very page). The server&#39;s job is just &lt;em&gt;read from disk and send bytes&lt;/em&gt;. Same answer for everyone, which makes static responses endlessly cacheable — by proxies, CDNs, and browsers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynamic&lt;/strong&gt; — the response is &lt;em&gt;computed per request&lt;/em&gt;: run a handler, maybe query a database, build HTML or JSON on the spot. Your dashboard and my dashboard come from the same URL but different data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is why real deployments split the work: a fast static server or CDN handles files, and your application only wakes up for the dynamic part.&lt;/p&gt;
&lt;h2 id=&quot;surviving-more-than-one-client&quot;&gt;Surviving more than one client&lt;/h2&gt;
&lt;p&gt;Here&#39;s the problem the loop hides: while your handler is busy computing one response, &lt;em&gt;new requests keep arriving&lt;/em&gt;. With a single worker, one slow request stops the whole site:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-concurrency&gt;&lt;/sim-concurrency&gt;&lt;/div&gt;
&lt;p&gt;The two classic escapes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;More workers&lt;/strong&gt; — threads or processes, each running the same loop. A slow request occupies one worker; the others keep serving. This is how Gunicorn/uWSGI-style servers run Python apps.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;An event loop&lt;/strong&gt; — one worker that never &lt;em&gt;waits&lt;/em&gt;: whenever a request is blocked on the database or disk, it sets the work aside and serves someone else (Node.js, nginx, Python&#39;s &lt;code&gt;asyncio&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If this sounds familiar, it should — it&#39;s the browser&#39;s main-thread lesson from &lt;a href=&quot;/web-fundamentals/how-browsers-work/&quot;&gt;How browsers work&lt;/a&gt;, mirrored on the server: &lt;strong&gt;whoever holds the only thread must never hold it long.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In production the pieces compose: a &lt;strong&gt;reverse proxy&lt;/strong&gt; (nginx, Caddy, a cloud load balancer) sits in front, serves static files, ends the TLS connection, and spreads dynamic requests across a pool of application workers. Every &quot;how do I deploy my app&quot; tutorial is some arrangement of exactly these boxes.&lt;/p&gt;
&lt;h2 id=&quot;takeaways&quot;&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A server is a loop&lt;/strong&gt;: accept → parse → route → handler → respond. Frameworks decorate the loop; they don&#39;t replace it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Routing is a lookup table&lt;/strong&gt;, and 404 is the table&#39;s fall-through working as designed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Static = read a file, dynamic = run code.&lt;/strong&gt; Push static to CDNs/caches; spend your server on the dynamic part.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;One worker means one slow request blocks everyone&lt;/strong&gt; — scale with worker pools or an event loop.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Real deployments layer it&lt;/strong&gt;: reverse proxy for static/TLS/balancing, application workers for the dynamic core.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Fielding, R., Nottingham, M., &amp;amp; Reschke, J. (2022a). &lt;em&gt;HTTP/1.1&lt;/em&gt; (RFC 9112). Internet Engineering Task Force. https://doi.org/10.17487/RFC9112&lt;/li&gt;
&lt;li&gt;Fielding, R., Nottingham, M., &amp;amp; Reschke, J. (2022b). &lt;em&gt;HTTP semantics&lt;/em&gt; (RFC 9110). Internet Engineering Task Force. https://doi.org/10.17487/RFC9110&lt;/li&gt;
&lt;li&gt;Kegel, D. (2006). &lt;em&gt;The C10K problem&lt;/em&gt;. Retrieved August 16, 2026, from http://www.kegel.com/c10k.html&lt;/li&gt;
&lt;li&gt;MDN Web Docs. (n.d.). &lt;em&gt;What is a web server?&lt;/em&gt; Mozilla. Retrieved August 16, 2026, from https://developer.mozilla.org/en-US/docs/Learn_web_development/Howto/Web_mechanics/What_is_a_web_server&lt;/li&gt;
&lt;li&gt;nginx. (n.d.). &lt;em&gt;Inside NGINX: How we designed for performance &amp;amp; scale&lt;/em&gt;. Retrieved August 16, 2026, from https://www.f5.com/company/blog/nginx/inside-nginx-how-we-designed-for-performance-scale&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>How HTTP works: the conversation behind every page</title>
        <published>2026-08-16T00:00:00+00:00</published>
        <updated>2026-08-16T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://learn.kevalabs.com/web-fundamentals/how-http-works/"/>
        <id>https://learn.kevalabs.com/web-fundamentals/how-http-works/</id>
        
        <content type="html" xml:base="https://learn.kevalabs.com/web-fundamentals/how-http-works/">&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt; none — this is the first stop on the web-fundamentals path. Everything else builds on it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Three facts explain almost everything about HTTP:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The client always speaks first.&lt;/strong&gt; A server can&#39;t call your browser out of the blue — every exchange starts with a request and ends with a response. Everything the web does is built from that one shape.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It&#39;s text you can read.&lt;/strong&gt; An HTTP/1.1 request is a few lines of plain text — you can type one by hand into a raw TCP connection and a real server will answer you. That simplicity is deliberate, and it&#39;s why the protocol was easy to extend for 30+ years.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It remembers nothing.&lt;/strong&gt; HTTP is stateless by design: each request arrives with no memory of the last one. Every login session, shopping cart, and &quot;stay signed in&quot; checkbox is a workaround built on top — with cookies.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&#39;s walk the conversation end to end. Each concept has a simulator — &lt;strong&gt;press play or step through them&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;who-s-talking-client-proxies-server&quot;&gt;Who&#39;s talking: client, proxies, server&lt;/h2&gt;
&lt;p&gt;HTTP is a client–server protocol. The &lt;em&gt;client&lt;/em&gt; (your browser, &lt;code&gt;curl&lt;/code&gt;, a mobile app — anything acting on your behalf) sends requests; the &lt;em&gt;server&lt;/em&gt; answers with documents or data. But between them, the request usually passes through &lt;em&gt;proxies&lt;/em&gt; — machines that can cache responses, filter content, balance load across servers, or log traffic, all without either end doing anything special:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-http-exchange&gt;&lt;/sim-http-exchange&gt;&lt;/div&gt;
&lt;p&gt;The cache hit at the end is the important part: a proxy that already holds a fresh copy can answer instantly without bothering the origin server. Much of web performance engineering is arranging for exactly that to happen — which is why HTTP has an entire vocabulary of caching headers.&lt;/p&gt;
&lt;h2 id=&quot;the-message-a-few-lines-of-text&quot;&gt;The message: a few lines of text&lt;/h2&gt;
&lt;p&gt;Here&#39;s a complete, real HTTP/1.1 exchange — first the request, then the response:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot; &gt;&lt;code data-lang=&quot;http&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;GET&lt;/span&gt;&lt;span&gt; /guide.html&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; HTTP&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;1.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;Host&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; learn.kevalabs.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;Accept-Language&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; en&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot; &gt;&lt;code data-lang=&quot;http&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;HTTP&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;1.1 200&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; OK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; Sat, 16 Aug 2026 07:28:00 GMT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;Server&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; keva-edge&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; text/html&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;Content-Length&lt;/span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; 29769&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;!doctype html&amp;gt;… (29,769 bytes of HTML)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every request and response has the same anatomy — a start line, headers, a blank line, and an optional body. Step through each part:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-http-message&gt;&lt;/sim-http-message&gt;&lt;/div&gt;
&lt;p&gt;Two details worth keeping: the &lt;code&gt;Host&lt;/code&gt; header is what lets one machine serve many websites from a single IP address, and headers in general are HTTP&#39;s extension mechanism — new capabilities (caching rules, CORS, compression, auth) arrive as new headers, not new protocols. HTTP/2 encodes all of this in binary frames for efficiency, but the &lt;em&gt;semantics&lt;/em&gt; are identical — what you learn here transfers unchanged.&lt;/p&gt;
&lt;h2 id=&quot;status-codes-the-server-s-verdict&quot;&gt;Status codes: the server&#39;s verdict&lt;/h2&gt;
&lt;p&gt;The first line of every response carries a three-digit status code. The first digit tells you who&#39;s happy and who&#39;s at fault:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-status-codes&gt;&lt;/sim-status-codes&gt;&lt;/div&gt;
&lt;p&gt;The classes matter more than memorizing individual codes: &lt;strong&gt;2xx&lt;/strong&gt; means success, &lt;strong&gt;3xx&lt;/strong&gt; means &quot;look elsewhere&quot; (the client follows automatically), &lt;strong&gt;4xx&lt;/strong&gt; means the &lt;em&gt;client&lt;/em&gt; asked for something wrong, &lt;strong&gt;5xx&lt;/strong&gt; means the &lt;em&gt;server&lt;/em&gt; broke while answering a valid request. That 4xx/5xx distinction is the first thing to check when debugging: it tells you which side of the wire to look at.&lt;/p&gt;
&lt;h2 id=&quot;stateless-by-design-sessions-by-cookies&quot;&gt;Stateless by design, sessions by cookies&lt;/h2&gt;
&lt;p&gt;HTTP treats every request as if it came from a stranger. That sounds like a flaw; it&#39;s actually what lets one server (or a hundred, behind a load balancer) handle millions of clients without keeping a conversation open for each. But then, how does a shopping cart survive from one click to the next?&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-http-cookies&gt;&lt;/sim-http-cookies&gt;&lt;/div&gt;
&lt;p&gt;The trick: the server hands the client a small token (&lt;code&gt;Set-Cookie&lt;/code&gt;), and the client volunteers it back on every subsequent request (&lt;code&gt;Cookie&lt;/code&gt;). The &lt;em&gt;client&lt;/em&gt; carries the memory; the server just uses the token to look up state on its side. Sessions, logins, and &quot;remember me&quot; are all this one mechanism.&lt;/p&gt;
&lt;h2 id=&quot;one-connection-or-many&quot;&gt;One connection or many&lt;/h2&gt;
&lt;p&gt;HTTP messages ride on TCP connections (with TLS on top for HTTPS), and how those connections are used changed dramatically across versions. Watch six resources being fetched under each model:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-http-connections&gt;&lt;/sim-http-connections&gt;&lt;/div&gt;
&lt;p&gt;HTTP/1.0 paid a full TCP handshake for &lt;em&gt;every&lt;/em&gt; resource. HTTP/1.1 introduced persistent connections — one handshake, requests one after another. HTTP/2 multiplexes: many requests and responses interleaved on a single connection at once, so one slow response no longer blocks the rest. HTTP/3 goes further, replacing TCP with QUIC so even the transport handshake all but disappears. (For why handshakes and round trips are so expensive in the first place, the next guide walks the full journey — &lt;a href=&quot;/web-fundamentals/how-browsers-work/&quot;&gt;How browsers work&lt;/a&gt;.)&lt;/p&gt;
&lt;h2 id=&quot;what-else-rides-on-headers&quot;&gt;What else rides on headers&lt;/h2&gt;
&lt;p&gt;Almost every other HTTP capability is &quot;just headers,&quot; which is the payoff of fact #2 — an extensible, readable protocol:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Caching&lt;/strong&gt; — &lt;code&gt;Cache-Control&lt;/code&gt;, &lt;code&gt;ETag&lt;/code&gt;, &lt;code&gt;Last-Modified&lt;/code&gt;: the server tells clients and proxies what may be stored, and for how long.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-origin access (CORS)&lt;/strong&gt; — &lt;code&gt;Origin&lt;/code&gt; and &lt;code&gt;Access-Control-*&lt;/code&gt;: servers selectively relax the browser&#39;s same-origin rules.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authentication&lt;/strong&gt; — &lt;code&gt;WWW-Authenticate&lt;/code&gt; / &lt;code&gt;Authorization&lt;/code&gt;, or cookie-based sessions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Content negotiation&lt;/strong&gt; — &lt;code&gt;Accept&lt;/code&gt;, &lt;code&gt;Accept-Language&lt;/code&gt;, &lt;code&gt;Content-Encoding&lt;/code&gt;: the same URL can serve French text or compressed bytes to the clients that ask for them.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;takeaways&quot;&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Everything is request → response, client-first.&lt;/strong&gt; If a design seems to need the server to speak first, you need a different mechanism (Server-Sent Events, WebSockets) — both of which start life as an HTTP request.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Read the raw messages when debugging.&lt;/strong&gt; HTTP is text (or binary framing of the same semantics); &lt;code&gt;curl -v&lt;/code&gt; shows you the actual conversation, and it settles arguments fast.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status class before status code&lt;/strong&gt;: 4xx → fix the request, 5xx → fix the server, 3xx → follow the trail.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cookies are the client carrying the server&#39;s memory.&lt;/strong&gt; Statelessness is a feature; treat session state as an explicit, deliberate add-on.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fewer connections, fewer handshakes.&lt;/strong&gt; Connection reuse and multiplexing are where whole round trips are won — the same lesson the next guide teaches from the browser&#39;s side.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Barth, A. (2011). &lt;em&gt;HTTP state management mechanism&lt;/em&gt; (RFC 6265). Internet Engineering Task Force. https://doi.org/10.17487/RFC6265&lt;/li&gt;
&lt;li&gt;Bishop, M. (2022). &lt;em&gt;HTTP/3&lt;/em&gt; (RFC 9114). Internet Engineering Task Force. https://doi.org/10.17487/RFC9114&lt;/li&gt;
&lt;li&gt;Fielding, R., Nottingham, M., &amp;amp; Reschke, J. (2022a). &lt;em&gt;HTTP/1.1&lt;/em&gt; (RFC 9112). Internet Engineering Task Force. https://doi.org/10.17487/RFC9112&lt;/li&gt;
&lt;li&gt;Fielding, R., Nottingham, M., &amp;amp; Reschke, J. (2022b). &lt;em&gt;HTTP semantics&lt;/em&gt; (RFC 9110). Internet Engineering Task Force. https://doi.org/10.17487/RFC9110&lt;/li&gt;
&lt;li&gt;MDN Web Docs. (n.d.). &lt;em&gt;Overview of HTTP&lt;/em&gt;. Mozilla. Retrieved August 16, 2026, from https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Overview&lt;/li&gt;
&lt;li&gt;Thomson, M., &amp;amp; Benfield, C. (2022). &lt;em&gt;HTTP/2&lt;/em&gt; (RFC 9113). Internet Engineering Task Force. https://doi.org/10.17487/RFC9113&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Structure follows MDN&#39;s &lt;a rel=&quot;external&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Overview&quot;&gt;Overview of HTTP&lt;/a&gt; (CC-BY-SA); the prose, mistakes, and simulators are ours.&lt;/em&gt;&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>How rendering works: server-side vs client-side</title>
        <published>2026-08-16T00:00:00+00:00</published>
        <updated>2026-08-16T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://learn.kevalabs.com/web-fundamentals/server-side-vs-client-side-rendering/"/>
        <id>https://learn.kevalabs.com/web-fundamentals/server-side-vs-client-side-rendering/</id>
        
        <content type="html" xml:base="https://learn.kevalabs.com/web-fundamentals/server-side-vs-client-side-rendering/">&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt; &lt;a href=&quot;/web-fundamentals/how-browsers-work/&quot;&gt;How browsers work&lt;/a&gt; (the rendering pipeline) and &lt;a href=&quot;/web-fundamentals/how-http-servers-work/&quot;&gt;How HTTP servers work&lt;/a&gt; (static vs dynamic responses).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A browser can only paint what &lt;a href=&quot;/web-fundamentals/how-browsers-work/&quot;&gt;its rendering pipeline&lt;/a&gt; is given: HTML. But your actual content — students, products, scores — lives in a database as &lt;em&gt;data&lt;/em&gt;. Somewhere between the database and the screen, &lt;strong&gt;someone has to turn data into HTML&lt;/strong&gt;. The entire server-side vs client-side rendering debate is one question:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Who builds the HTML — the server before sending, or the browser after arriving?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;the-race-same-page-two-strategies&quot;&gt;The race: same page, two strategies&lt;/h2&gt;
&lt;p&gt;Watch both approaches deliver the same student list, side by side:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-render-race&gt;&lt;/sim-render-race&gt;&lt;/div&gt;
&lt;p&gt;The shape of the result is worth memorizing. &lt;strong&gt;SSR&lt;/strong&gt; does its work &lt;em&gt;before&lt;/em&gt; the first byte leaves: slower to start responding, but what arrives is immediately paintable. &lt;strong&gt;CSR&lt;/strong&gt; responds instantly with a nearly-empty shell — great first byte! — then makes the user wait through a JS download, its execution, &lt;em&gt;and a second round trip&lt;/em&gt; for data before anything real appears. On fast Wi-Fi you barely notice; on a phone on 4G, the CSR gap is very visible.&lt;/p&gt;
&lt;h2 id=&quot;what-actually-travels-the-wire&quot;&gt;What actually travels the wire&lt;/h2&gt;
&lt;p&gt;The race explains &lt;em&gt;when&lt;/em&gt;; this explains &lt;em&gt;what&lt;/em&gt;. Step through the actual responses each strategy sends:&lt;/p&gt;
&lt;div class=&quot;sim-embed&quot;&gt;&lt;sim-wire-view&gt;&lt;/sim-wire-view&gt;&lt;/div&gt;
&lt;p&gt;That difference — content &lt;em&gt;in&lt;/em&gt; the HTML vs content arriving separately as JSON — has consequences beyond speed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;View-source is the SSR litmus test.&lt;/strong&gt; If your data is in the page source, the server rendered it. (Your lab this week proves it — see below.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Robots and previews read HTML.&lt;/strong&gt; Search crawlers and link-preview bots get the SSR page&#39;s content for free; a CSR page shows them an empty &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; unless extra machinery steps in.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No JavaScript, no CSR.&lt;/strong&gt; SSR pages degrade gracefully; a CSR page without its bundle is a blank rectangle.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;so-which-one-the-honest-trade-offs&quot;&gt;So which one? (The honest trade-offs)&lt;/h2&gt;
&lt;p&gt;Neither wins outright — they optimize different moments:&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;aspect&lt;/th&gt;&lt;th&gt;server-side (SSR)&lt;/th&gt;&lt;th&gt;client-side (CSR)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;First meaningful paint&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Fast&lt;/strong&gt; — the HTML arrives ready to paint&lt;/td&gt;&lt;td&gt;Slow — bundle download, then a JSON fetch, first&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Navigating after load&lt;/td&gt;&lt;td&gt;Full page reload on every click&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Instant, app-like&lt;/strong&gt; updates&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Server cost per view&lt;/td&gt;&lt;td&gt;Renders every page view (CPU per request)&lt;/td&gt;&lt;td&gt;Serves static files + JSON (cheap)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;SEO &amp;amp; link previews&lt;/td&gt;&lt;td&gt;Content visible to crawlers for free&lt;/td&gt;&lt;td&gt;Needs extra machinery (prerendering)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;With JavaScript off&lt;/td&gt;&lt;td&gt;Fully readable&lt;/td&gt;&lt;td&gt;Blank page&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The rule of thumb: &lt;strong&gt;content sites lean SSR&lt;/strong&gt; (blogs, docs, shops — first paint and SEO dominate), &lt;strong&gt;tools lean CSR&lt;/strong&gt; (dashboards, editors — you load once, then interact for an hour). And the modern mainstream is deliberately both: frameworks like Next.js render the first view on the server, then &lt;em&gt;hydrate&lt;/em&gt; — attach JavaScript so subsequent interaction behaves like CSR. Once you understand the two pure strategies, hybrids are just mixing the timelines you watched above.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Now prove it with your own hands:&lt;/strong&gt; &lt;a href=&quot;/python/server-side-rendering-in-pure-python/&quot;&gt;Server-side rendering in pure Python&lt;/a&gt; builds the SSR half in ~45 lines of standard library, no framework.&lt;/p&gt;
&lt;h2 id=&quot;takeaways&quot;&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Rendering = turning data into HTML.&lt;/strong&gt; The only question is where it happens.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSR&lt;/strong&gt;: server sends finished HTML → fast first paint, view-source shows content, works without JS, costs server CPU per view.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CSR&lt;/strong&gt;: server sends a shell + bundle; the browser fetches JSON and builds the DOM → slow first content, excellent app-like feel afterwards.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check view-source&lt;/strong&gt; when you meet a new site — it tells you the rendering strategy in five seconds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Modern frameworks hybridize&lt;/strong&gt; (SSR first paint + hydration); you now have the vocabulary to read their docs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;MDN Web Docs. (n.d.). &lt;em&gt;Introduction to the server side&lt;/em&gt;. Mozilla. Retrieved August 16, 2026, from https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Server-side/First_steps/Introduction&lt;/li&gt;
&lt;li&gt;Osmani, A., Miller, J., &amp;amp; Grigorik, I. (2019). &lt;em&gt;Rendering on the web&lt;/em&gt;. web.dev. Retrieved August 16, 2026, from https://web.dev/articles/rendering-on-the-web&lt;/li&gt;
&lt;li&gt;Vercel. (n.d.). &lt;em&gt;Rendering: server-side rendering (SSR)&lt;/em&gt; (Next.js documentation). Retrieved August 16, 2026, from https://nextjs.org/docs/pages/building-your-application/rendering/server-side-rendering&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
</feed>
