DNS, HTTP & TLS — From a URL to a Page
What you'll learn
- How DNS turns a name such as
www.example.cominto an address, and the record types you will meet (A, AAAA, CNAME, MX, NS, TXT and more) - What an HTTP request and response really look like on the wire: the request line, headers, status codes and the body
- How TLS proves you are talking to the real server with a certificate, and then encrypts the conversation
- How to read the errors each step produces, from
NXDOMAINto "certificate expired" - You will build three tools: a DNS lookup that follows aliases, an HTTP response parser and a certificate chain checker
The phone book, the letter and the sealed envelope ✉️
You want to send a letter to "Example Bookshop", but the post office only understands street addresses. So first you look the name up in a phone book: that is DNS. Then you write the letter in a format the shop understands, with a clear subject line and your request: that is HTTP. Finally, because the letter travels through many hands, you put it in a sealed envelope that only the shop can open, and you check the shop's official stamp before you trust the reply: that is TLS.
Every time you open a page, your device does these three things in order, usually in well under a second:
https://www.example.com/docs/
| | |
| | +-- path: which page on that server
| +------------------ host: looked up in DNS
+------------------------- scheme: https = HTTP inside TLS, port 443 by default
1. DNS www.example.com -> 192.0.2.10
2. TCP three-way handshake with 192.0.2.10:443
3. TLS check the certificate, agree on keys
4. HTTP GET /docs/ -> 200 OK + the HTML
5. Repeat for every image, script and stylesheet the page needs
You already know step 2 from the TCP lesson. This lesson covers the other three.
DNS: the Internet's phone book
DNS (Domain Name System, RFC 1034 and RFC 1035) is a distributed database. No single server knows every name. Instead, names form a tree read from right to left:
. (root)
/ | \
com org sa <- top-level domains (TLDs)
|
example <- example.com, delegated to its owner
/ \
www mail <- www.example.com, mail.example.com
Each level delegates the level below it. The root knows who runs com; the com servers know which servers are authoritative for example.com; those servers hold the actual records. The root is served by 13 named server identities, a.root-servers.net to m.root-servers.net, each replicated in many places around the world.
How a lookup travels
Your computer runs a small stub resolver that asks one recursive resolver (from your ISP, your company, or a public one) to do the work. If the answer is not in its cache, the recursive resolver walks the tree with iterative queries:
you -> recursive resolver: "www.example.com, type A?"
resolver -> root server: "ask the com servers" (referral)
resolver -> com server: "ask ns1.example.com" (referral)
resolver -> ns1.example.com: "www.example.com A 192.0.2.10" (answer, TTL 300)
you <- recursive resolver: 192.0.2.10
Every answer carries a TTL (time to live) in seconds: how long it may be cached. That is why DNS is fast (most lookups never leave the resolver) and also why a change to a record takes time to reach everyone. Even "this name does not exist" is cached, for a time taken from the zone's SOA record (RFC 2308).
DNS normally uses UDP port 53: one small question, one small answer. It falls back to TCP port 53 for large responses and zone transfers. Classic DNS is neither encrypted nor authenticated, so two additions exist: DNSSEC (RFC 4033) signs records so a resolver can detect forged answers, and DNS over TLS (port 853, RFC 7858) and DNS over HTTPS (RFC 8484) hide your queries from people on the network path.
The first lookup walks root, com and the authoritative server. Sixty seconds later the answer comes straight from the cache. At 400 s the TTL of 300 s has run out, so the resolver walks the tree again. The last name has no record at the authoritative server: that is NXDOMAIN. A real resolver also caches the referrals, so it would skip root and com and ask ns1.example.com directly. Try a TTL of 30 and watch the second lookup miss the cache.
# A toy DNS tree held in a dictionary: nothing is sent over the network. The root and
# .com server names are the real ones; the zone data and the addresses are made up, from the
# names and ranges reserved for documentation (RFC 2606, RFC 5737).
SERVERS = {
"a.root-servers.net": {"com.": ("referral", "a.gtld-servers.net")},
"a.gtld-servers.net": {"example.com.": ("referral", "ns1.example.com")},
"ns1.example.com": {"www.example.com.": ("answer", "192.0.2.10"),
"mail.example.com.": ("answer", "192.0.2.25")},
}
TTL = 300
cache = {} # name -> (address, expires_at)
def ask(server, name):
"""The server answers from the most specific zone it knows about."""
best = None
for suffix, reply in SERVERS[server].items():
if name == suffix or name.endswith("." + suffix):
if best is None or len(suffix) > len(best[0]):
best = (suffix, reply)
return best[1] if best else ("nxdomain", None)
def resolve(name, now):
if name in cache and cache[name][1] > now:
print(" cache hit, expires in", cache[name][1] - now, "s ->", cache[name][0])
return cache[name][0]
server = "a.root-servers.net" # every resolver starts from the root hints
while True:
kind, data = ask(server, name)
print(" ask " + server.ljust(19) + " -> " + kind + (" " + data if data else ""))
if kind == "referral":
server = data
continue
if kind == "answer":
cache[name] = (data, now + TTL)
return data
for now, name in [(0, "www.example.com."), (60, "www.example.com."), (400, "www.example.com."), (400, "shop.example.com.")]:
print("t=" + str(now) + "s resolve " + name)
resolve(name, now)
Record types and answers
A zone is a list of resource records: name TTL class type data. The types you will use most:
- A: an IPv4 address. AAAA: an IPv6 address (RFC 3596). A name can have several of each; clients pick one and try the others if it fails.
- CNAME: "this name is an alias of that one". The resolver restarts the lookup at the target. A name that has a CNAME must have no other records (RFC 1034), which is why the bare zone name (
example.com) cannot be a CNAME. - MX: mail servers for the domain, each with a preference number; lower is tried first.
- NS: the authoritative name servers of a zone (this is how delegation is written down).
- TXT: free text, used for things like SPF mail policy and proving domain ownership.
- PTR: reverse lookup, address to name, under
in-addr.arpaandip6.arpa. - SOA: start of authority: the zone's serial number and timers.
- CAA: which certificate authorities may issue certificates for the domain (RFC 8659).
dig shows the whole answer. A typical session (the addresses are from documentation ranges):
$ dig www.example.com A
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4721
;; QUESTION SECTION:
;www.example.com. IN A
;; ANSWER SECTION:
www.example.com. 300 IN CNAME web.example.net.
web.example.net. 60 IN A 192.0.2.10
;; Query time: 23 msec
Learn to read the status:
- NOERROR with records: success. NOERROR with an empty answer is called NODATA: the name exists but has no record of that type (for example a domain with MX records but no A record).
- NXDOMAIN: the name does not exist at all. Check for typos first.
- SERVFAIL: the resolver could not get a usable answer: the authoritative servers are unreachable, misconfigured, or DNSSEC validation failed.
- REFUSED: the server will not answer you, for example a resolver that only serves its own network.
Useful variants: dig +short name for just the data, dig @192.0.2.53 name to ask a specific server, dig +trace name to walk the tree yourself, and dig -x 192.0.2.10 for reverse lookups.
Lab exercise 1
PythonGraded in Code LabWrite the answering logic of a small DNS server that follows CNAME aliases.
Input: the first line is N, the number of records. The next N lines are records <name> <TYPE> <value> (the value is the rest of the line, so an MX value such as 10 mail.example.com keeps its space). Every following non-blank line is a query <name> <TYPE>.
Rules
- Names are case-insensitive and a trailing dot is ignored:
WWW.Example.COM.iswww.example.com. Print names in lower case without the trailing dot. Print values exactly as given. - If the name has records of the asked type, the answer is all their values in input order, joined with
,. (A query for typeCNAMEtherefore returns the alias itself.) - Otherwise, if the name has a CNAME, continue with its target and remember it in the chain.
- Otherwise, if the name has no records at all, the answer is
NXDOMAIN; if it has records of other types only, the answer isNODATA. - If the chain comes back to a name already visited, the answer is
SERVFAIL (cname loop)(and no chain is printed).
Output, one line per query: <name> <TYPE>: <answer>, followed by (via <target1> -> <target2> ...) when at least one CNAME was followed.
Example
Input:
3
shop.example.com CNAME edge.example.net
edge.example.net A 198.51.100.7
example.com MX 10 mail.example.com
shop.example.com A
example.com A
nope.example.com A
Output:
shop.example.com A: 198.51.100.7 (via edge.example.net)
example.com A: NODATA
nope.example.com A: NXDOMAIN
- Direct answers, several values in input order
- A two-step CNAME chain; a CNAME query returns the alias itself
- NODATA versus NXDOMAIN, also at the end of a chain
- Names ignore case and a trailing dot
- A CNAME loop is a SERVFAIL
- Empty zone: every name is NXDOMAIN
Example run
- Input
5 www.example.com A 192.0.2.10 www.example.com A 192.0.2.11 www.example.com AAAA 2001:db8::10 example.com MX 10 mail.example.com example.com MX 20 backup.example.net www.example.com A www.example.com AAAA example.com MX
- Expected output
www.example.com A: 192.0.2.10, 192.0.2.11 www.example.com AAAA: 2001:db8::10 example.com MX: 10 mail.example.com, 20 backup.example.net
import sys
lines = sys.stdin.read().split("\n")
count = int(lines[0]) if lines and lines[0].strip() else 0
# name -> list of (TYPE, value), in input order. Names are case-insensitive; a trailing dot is dropped.
records = {}
for line in lines[1:1 + count]:
name, rtype, value = line.split(None, 2)
records.setdefault(name.lower().rstrip("."), []).append((rtype.upper(), value.strip()))
More lines in Code Lab: 25
Hints and instant feedback are in Code Lab. Optional practice: it does not block finishing the lesson.
JavaScript and Python also run in your browser without an account; other languages use the secure server runner and need a free account.
HTTP: a conversation in plain text
HTTP is the request/response protocol of the web. Its meaning (methods, status codes, headers) is defined once in RFC 9110 and carried by three wire formats: HTTP/1.1 (RFC 9112, text over TCP), HTTP/2 (RFC 9113, binary frames over one TCP connection) and HTTP/3 (RFC 9114, over QUIC on UDP). HTTP/1.1 is the easiest to read, so we start there. Every line ends with CRLF (\r\n), and an empty line ends the headers:
GET /docs/ HTTP/1.1 <- request line: method, target, version
Host: www.example.com <- required in HTTP/1.1
User-Agent: curl/8.5.0
Accept: text/html
<- empty line: end of the headers
HTTP/1.1 200 OK <- status line: version, status code, reason
Content-Type: text/html; charset=utf-8
Content-Length: 1256 <- the body is exactly 1256 bytes
Cache-Control: max-age=3600
<- empty line
<!doctype html> ... <- the body
Methods say what you want: GET reads, HEAD reads only the headers, POST submits data, PUT replaces, PATCH modifies, DELETE removes. GET and HEAD are safe (they must not change anything), and PUT and DELETE are idempotent (repeating them has the same effect as doing them once), which is why a client may retry them.
Status codes are grouped by their first digit:
- 1xx informational: for example
101 Switching Protocolswhen a connection upgrades to WebSocket. - 2xx success:
200 OK,201 Created,204 No Content. - 3xx redirection:
301 Moved Permanently,302 Found,304 Not Modified(your cached copy is still good),307and308. The new address is in theLocationheader. - 4xx client error:
400 Bad Request,401 Unauthorized(you are not logged in),403 Forbidden(you are logged in but not allowed),404 Not Found,429 Too Many Requests. - 5xx server error:
500 Internal Server Error,502 Bad Gatewayand504 Gateway Timeout(a proxy could not get a good answer from the server behind it),503 Service Unavailable.
Headers are Name: value lines, and their names are case-insensitive: content-length and Content-Length are the same field. The reason phrase after the code is informational only; programs must decide by the number. How long is the body? Content-Length gives its size in bytes (not characters: an Arabic letter is two bytes in UTF-8); Transfer-Encoding: chunked sends it in pieces; with neither, the body ends when the server closes the connection. Responses to HEAD, and 1xx, 204 and 304 responses never have a body. Getting this framing wrong is dangerous: when a proxy and a server disagree about where a message ends, an attacker can hide a second request inside the first (request smuggling), which is why a message with two different Content-Length values must be rejected (RFC 9112, section 6.3).
HTTP itself is stateless: each request stands alone. Logins and shopping carts work because the server sets a cookie (Set-Cookie) and the browser sends it back (Cookie) with every later request.
The JSON form shows the invisible \r\n pairs and the empty line that ends the request. The parser lowercases header names, so LOCATION and Location are found the same way, and it decides by the number 301, not by the words after it. The last line is why Content-Length must count bytes. Change the status to 404 Not Found and run it again.
// HTTP/1.1 is text: build a request exactly as it travels, CRLF line endings included.
const CRLF = "\r\n";
const request = ["GET /docs/ HTTP/1.1", "Host: www.example.com", "Accept: text/html", "", ""].join(CRLF);
console.log("request bytes:", request.length);
console.log(JSON.stringify(request));
// A response the server might send back, parsed the way a client does it.
const response = "HTTP/1.1 301 Moved Permanently\r\nLOCATION: /docs/start/\r\ncontent-length: 0\r\n\r\n";
const head = response.split("\r\n\r\n")[0].split("\r\n");
const statusLine = head[0].split(" ");
const headers = {};
head.slice(1).forEach(function (line) {
const colon = line.indexOf(":");
headers[line.slice(0, colon).toLowerCase()] = line.slice(colon + 1).trim(); // names are case-insensitive
});
const classes = { 1: "informational", 2: "success", 3: "redirection", 4: "client error", 5: "server error" };
const code = statusLine[1];
console.log("version " + statusLine[0] + ", status " + code + " -> " + classes[code[0]]);
console.log("follow the redirect to:", headers["location"]);
console.log("body length:", headers["content-length"], "bytes");
console.log("'مرحبا' is", "مرحبا".length, "characters but", new TextEncoder().encode("مرحبا").length, "bytes in UTF-8");
Lab exercise 2
JavaScript (Node.js)Graded in Code LabWrite the first thing every HTTP client does: parse a raw HTTP/1.1 response read from stdin.
The header block ends at the first empty line; everything after that line is the body, exactly as it arrived. Lines end with \n, possibly preceded by \r (remove the \r). The starter already splits the input into head (the header lines) and body.
Rules
- The status line must look like
HTTP/<digit>.<digit> <3-digit code>, optionally followed by a space and a reason phrase. The first digit of the code must be 1 to 5. Otherwise print onlyerror: bad status line. - Class names: 1
informational, 2success, 3redirection, 4client error, 5server error. Ignore the reason phrase. - Header names are case-insensitive; values are trimmed. If a header repeats, the first one counts, with one exception: two
Content-Lengthheaders with different values leave the end of the body ambiguous (the opening for request smuggling), so print onlyerror: conflicting content-length(RFC 9112, section 6.3, makes this an unrecoverable error). The same value repeated is fine. Check the status line first. - The body:
nonefor 1xx, 204 and 304 responses. Otherwise, with aContent-Lengthof L: if at least L bytes (UTF-8) arrived,<L> bytes; if fewer,incomplete (<got>/<L> bytes). Without Content-Length:<got> bytes (until close).
Output
status=<code> class=<class>
headers=<number of header lines>
content-type=<value, or none>
location=<value> (only when there is a Location header)
body=<description>
Example
Input:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 12
<p>Hello</p>
Output:
status=200 class=success
headers=2
content-type=text/html
body=12 bytes
- A redirect with CRLF line endings, odd capitals, extra spaces and repeated headers (the first one counts)
- 304 Not Modified never has a body, whatever Content-Length says
- Content-Length counts bytes: 5 Arabic letters are 10 bytes, so 12 is incomplete
- No Content-Length: the body runs until the connection closes
- Two different Content-Length values are an error (request smuggling)
- An invalid status line is an error
Example run
- Input
HTTP/1.1 301 Moved Permanently LOCATION: /new-home content-LENGTH: 5 Server: demo Location: /old-home Content-Length: 5 moved
- Expected output
status=301 class=redirection headers=5 content-type=none location=/new-home body=5 bytes
const raw = require("fs").readFileSync(0, "utf8");
const CLASSES = { 1: "informational", 2: "success", 3: "redirection", 4: "client error", 5: "server error" };
// Split the message into header lines and the body: the header block ends at the first empty line.
// Lines may end with "\r\n" (real HTTP) or "\n"; the body is kept exactly as it arrived.
const head = [];
let pos = 0;
let body = "";
while (pos < raw.length) {
let nl = raw.indexOf("\n", pos);
if (nl === -1) nl = raw.length;More lines in Code Lab: 25
Hints and instant feedback are in Code Lab. Optional practice: it does not block finishing the lesson.
JavaScript and Python also run in your browser without an account; other languages use the secure server runner and need a free account.
TLS: who are you, and can anyone listen?
Plain HTTP crosses every router, Wi-Fi access point and proxy in clear text: anyone on the path can read it or change it. TLS (Transport Layer Security) fixes three things at once: confidentiality (nobody can read the data), integrity (nobody can change it unnoticed) and authentication (you are talking to the real www.example.com, not an impostor). HTTPS is simply HTTP inside TLS, on port 443. The current version is TLS 1.3 (RFC 8446); TLS 1.2 (RFC 5246) is still common, while TLS 1.0 and 1.1 are deprecated (RFC 8996), SSL even longer ago, and all of them should be switched off.
The TLS 1.3 handshake
After the TCP handshake, client and server need one round trip to set up TLS 1.3:
Client Server
ClientHello -------->
supported versions, cipher suites,
key_share (client's public DH key),
server_name = www.example.com (SNI)
<-------- ServerHello
key_share (server's public DH key)
{EncryptedExtensions}
{Certificate}
{CertificateVerify}
{Finished}
{Finished} -------->
[GET /docs/ HTTP/1.1 ...] <-------> [HTTP/1.1 200 OK ...]
{ } encrypted with handshake keys [ ] encrypted with application keys
- Both sides send a fresh ephemeral Diffie-Hellman public key (
key_share), classically X25519; current browsers such as Chrome and Firefox send the post-quantum hybrid X25519MLKEM768 by default, which pairs X25519 with ML-KEM-768 (FIPS 203) so that traffic recorded today stays safe from a future quantum computer. From the two key shares, each side computes the same secret without it ever crossing the wire. Because the keys are thrown away afterwards, recording the traffic and stealing the server's private key later still does not decrypt it: this is forward secrecy. - The server sends its certificate and a CertificateVerify: a signature over the handshake made with the private key that belongs to the certificate. Only the real owner of that key can produce it.
- Both send Finished, a MAC over the whole handshake, so any tampering with the earlier messages is detected.
- Application data is protected with an AEAD cipher (AES-GCM or ChaCha20-Poly1305), which encrypts and authenticates every record.
One detail matters for privacy: the SNI (the host name) is sent in the ClientHello before encryption starts, so an observer can see which site you visit, though not which pages. An extension called Encrypted Client Hello (ECH) is designed to hide it.
Certificates and the chain of trust
A certificate (X.509, RFC 5280) binds a public key to names. Its key fields are the subject, the issuer (who signed it), the validity period (notBefore and notAfter), the public key, and the Subject Alternative Name (SAN) list of DNS names it is valid for. A certificate authority (CA) signs it. Your operating system or browser ships a trust store of root CA certificates. Servers send their own (leaf) certificate plus the intermediate certificates, and the client builds the chain:
www.example.com issuer: Example Issuing CA 1 (leaf, sent by the server)
^ signed by
Example Issuing CA 1 issuer: Example Root CA (intermediate, sent by the server)
^ signed by
Example Root CA self-signed (root, already in the trust store)
The client accepts the server only if all of these hold:
- Every signature in the chain verifies, and every certificate that signs another one is marked as a CA.
- The chain ends at a root in the trust store.
- Today's date is inside every certificate's validity period.
- The host name matches one of the leaf's SAN entries. Matching ignores case, and a wildcard such as
*.example.comstands for exactly one whole label on the left: it matchesapi.example.combut notexample.comora.b.example.com(RFC 9525). Browsers no longer look at the old Common Name field. - The certificate has not been revoked (checked with OCSP or CRLs, or with the browser's own revocation lists).
Public certificates are deliberately short-lived. The CA/Browser Forum is cutting their maximum lifetime in steps, from 398 days to 200 days in March 2026 and down to 47 days by 2029, so renewal must be automatic, usually with the ACME protocol (RFC 8555) used by CAs such as Let's Encrypt. The classic outage is still "the certificate expired on Sunday". The openssl s_client -connect www.example.com:443 -servername www.example.com command prints the chain a server sends, and curl -v shows the handshake and any validation error.
Lab exercise 3
GoGraded in Code LabWrite the certificate checks a TLS client runs before it trusts a server. Real clients also verify every cryptographic signature; here the subject and issuer names stand in for those signatures.
Input, one item per line:
root <name>: a root CA in the trust store (there may be several).cert <subject>|<issuer>|<notBefore>|<notAfter>|<yes or no: is it a CA>|<SANs, comma-separated, may be empty>: the chain the server sent, leaf first. Dates areYYYY-MM-DD, valid from notBefore to notAfter inclusive.check <host> <date>: validate the chain for that host name on that day.
For each check, report the first problem found, testing in this order:
- No
certlines at all:FAIL no certificate. - The host must match one SAN of the leaf: case-insensitive, and
*.at the start stands for exactly one whole label (anything else, such asw*.example.com, only matches literally). OtherwiseFAIL hostname mismatch. - For each certificate in order:
FAIL not yet valid: <subject>orFAIL expired: <subject>. - For each certificate after the leaf: its subject must equal the issuer of the one before it, otherwise
FAIL broken chain after: <subject of the one before>; and it must be a CA, otherwiseFAIL not a CA: <subject>. - The issuer of the last certificate must be a trusted root, otherwise
FAIL untrusted root: <issuer>.
If everything passes, the result is OK. Output one line per check: <host> <date>: <result>.
Example
Input:
root Example Root CA
cert www.example.com|Example Issuing CA 1|2026-06-01|2026-12-28|no|www.example.com,example.com
cert Example Issuing CA 1|Example Root CA|2024-01-01|2030-12-31|yes|
check www.example.com 2026-10-17
check shop.example.com 2026-10-17
check www.example.com 2026-12-29
Output:
www.example.com 2026-10-17: OK
shop.example.com 2026-10-17: FAIL hostname mismatch
www.example.com 2026-12-29: FAIL expired: www.example.com
- The worked example: names, the last valid day, expired and not yet valid
- A wildcard covers exactly one label; a partial wildcard covers nothing
- A missing intermediate breaks the chain
- An expired intermediate fails the whole chain; sending the root as well is fine
- A certificate that is not a CA cannot sign another one
- Unknown root: a self-signed certificate is not trusted
Example run
- Input
root Example Root CA cert www.example.com|Example Issuing CA 1|2026-06-01|2026-12-28|no|www.example.com,example.com cert Example Issuing CA 1|Example Root CA|2024-01-01|2030-12-31|yes| check www.example.com 2026-10-17 check EXAMPLE.com 2026-10-17 check shop.example.com 2026-10-17 check www.example.com 2026-12-28 check www.example.com 2026-12-29 check www.example.com 2026-05-31
- Expected output
www.example.com 2026-10-17: OK EXAMPLE.com 2026-10-17: OK shop.example.com 2026-10-17: FAIL hostname mismatch www.example.com 2026-12-28: OK www.example.com 2026-12-29: FAIL expired: www.example.com www.example.com 2026-05-31: FAIL not yet valid: www.example.com
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
type cert struct {
subject, issuer string
notBefore, notAfter string // YYYY-MM-DD, so plain string comparison orders them correctlyMore lines in Code Lab: 58
Hints and instant feedback are in Code Lab. Optional practice: it does not block finishing the lesson.
JavaScript and Python also run in your browser without an account; other languages use the secure server runner and need a free account.
When the page does not load: which step failed?
Because the steps happen in order, the error tells you where to look:
- "Server not found" /
NXDOMAIN: DNS. Check the spelling, thendig name. ASERVFAILpoints at the domain's name servers or DNSSEC, not at you. - "Connection refused" or a timeout: TCP. The name resolved, but nothing listens on that port, or a firewall drops the packets (you met this difference in the TCP lesson).
- Certificate errors: TLS. Expired means renewal failed; name mismatch means the certificate does not list this host (often a missing
wwwor a wildcard used one level too deep); unknown issuer means the server forgot to send its intermediate, or the certificate is self-signed. - A 4xx or 5xx status: HTTP. The connection worked; the server answered with an error. 4xx says the request is wrong (bad URL, missing login); 5xx says the server or something behind it failed.
The curl -v https://www.example.com/ command shows all four stages in one go: the resolved address, the TCP connection, the TLS handshake with the certificate subject and issuer, and the raw request and response headers.
Question 1 of 5
`dig example.com A` returns status NOERROR with an empty answer section. What does that mean?
Question 2 of 5
You changed the A record of your site an hour ago, but some users still reach the old server. What is the most likely reason?
Question 3 of 5
A user is logged in but opens a page they are not allowed to see. Which status code fits best?
Question 4 of 5
A certificate lists only `*.example.com` in its SAN. For which host name is it valid?
Question 5 of 5
Why does TLS 1.3 use ephemeral Diffie-Hellman keys for every connection?
Recap
- DNS is a distributed tree: root, TLD, authoritative servers. A recursive resolver walks it and caches every answer for its TTL. Know A, AAAA, CNAME, MX, NS, TXT, PTR, SOA, CAA, and the statuses NOERROR, NODATA, NXDOMAIN, SERVFAIL, REFUSED.
- HTTP is request/response: method, target, headers, empty line, body. Status codes group by first digit; header names ignore case;
Content-Lengthcounts bytes; 1xx, 204 and 304 have no body. - TLS 1.3 needs one round trip, uses ephemeral Diffie-Hellman for forward secrecy, and authenticates the server with a certificate checked for chain, CA flags, dates, SAN name and revocation.
- The error tells you the step: name, connection, certificate or HTTP status.
Practice next: run dig +trace on a domain you use every day, then curl -v on its home page and find the TLS version, the certificate issuer and the response headers. Next lesson: how routers and switches move all of this traffic.
Practice
Reading is not learning until you run something. Pick one.
Graded lab exercises
- Lab exercise 1PythonNot passed yet Try in Code Lab
- Lab exercise 2JavaScript (Node.js)Not passed yet Try in Code Lab
- Lab exercise 3GoNot passed yet Try in Code Lab