HTTP & HTTPS Explained
Almost everything you do on the web rides on HTTP (HyperText Transfer Protocol — the language browsers and servers use to talk). When you open a website, your browser sends an HTTP request and the server sends back an HTTP response. HTTPS is the same protocol with a layer of encryption added on top. As a DevOps engineer you will spend a lot of time reading these requests and responses to figure out why a site is slow, broken, or returning the wrong page — so it pays to understand them deeply.
The request/response model
HTTP is a request/response protocol. The client (usually a browser, but it can be a command-line tool like curl) asks for something, and the server replies. Each exchange is independent — the server does not, by default, remember anything about previous requests. This is called being stateless. Cookies and sessions are bolted on later to add memory.
A single request has four parts:
- A method (also called a verb) — what you want to do, like
GETorPOST. - A path — which resource on the server, like
/index.htmlor/api/users. - Headers — extra key/value information about the request (who is asking, what formats they accept, etc.).
- An optional body — data you are sending, used mostly with
POSTandPUT.
The response mirrors this: a status code (a number saying how it went), headers, and usually a body (the HTML page, JSON data, image, and so on).
Common HTTP methods
The method tells the server your intent. The two you will see most are GET and POST.
| Method | What it does | Has a body? | When to use it |
|---|---|---|---|
GET | Fetch a resource. Should not change anything. | No | Loading a page, reading data from an API. |
POST | Send data to create something. | Yes | Submitting a form, creating a new record. |
PUT | Replace a resource entirely. | Yes | Updating a record when you send the full object. |
PATCH | Update part of a resource. | Yes | Changing one field of a record. |
DELETE | Remove a resource. | No (usually) | Deleting a record. |
HEAD | Like GET but returns headers only, no body. | No | Checking if a page exists or its size, cheaply. |
Treat
GETas read-only. AGETrequest should never change data on the server. Search engines and browsers freely re-sendGETrequests, so if aGETdeletes something you will lose data by accident.
Status code families
The server’s reply always starts with a three-digit status code. The first digit tells you the broad category, so learning the five families gets you 90% of the way.
| Family | Meaning | Common examples |
|---|---|---|
| 1xx | Informational — request received, still processing. | 100 Continue |
| 2xx | Success — it worked. | 200 OK, 201 Created, 204 No Content |
| 3xx | Redirection — go look somewhere else. | 301 Moved Permanently, 302 Found, 304 Not Modified |
| 4xx | Client error — you (the caller) did something wrong. | 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found |
| 5xx | Server error — the server broke. | 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable |
The most useful debugging rule: 4xx means fix your request, 5xx means fix the server. For example, a 502 Bad Gateway from Nginx almost always means your application behind it (a reverse proxy is a server that sits in front of your app and forwards requests to it) is down or crashed — check the app, not Nginx.
Headers
Headers carry metadata. A few you will meet constantly:
Host— which website you want (one server can host many). Required in HTTP/1.1.User-Agent— what client is asking (browser name, orcurl/8.5.0).Content-Type— the format of the body, e.g.application/jsonortext/html.Authorization— credentials, e.g. a bearer token for an API.Cache-Control— how long the response may be cached.Location— where to go next (sent with 3xx redirects).
Inspecting a request with curl
curl is a command-line tool for making HTTP requests, perfect for debugging from a server where there is no browser. The -v (verbose) flag shows the full request and response, including headers. Install it on Ubuntu if it is missing:
sudo apt update
sudo apt install -y curl
Now make a verbose request:
curl -v https://example.com
Output:
* Trying 93.184.216.34:443...
* Connected to example.com (93.184.216.34) port 443
* ALPN: server accepted h2
* Server certificate:
* subject: CN=example.com
* start date: Jan 30 00:00:00 2026 GMT
* expire date: Mar 1 23:59:59 2027 GMT
* issuer: C=US; O=DigiCert Inc; CN=DigiCert TLS RSA SHA256 2020 CA1
* SSL certificate verify ok.
> GET / HTTP/2
> Host: example.com
> user-agent: curl/8.5.0
> accept: */*
>
< HTTP/2 200
< content-type: text/html; charset=UTF-8
< date: Mon, 15 Jun 2026 10:12:44 GMT
< cache-control: max-age=604800
< content-length: 1256
<
<!doctype html>
<html>
...
Lines starting with > are what you sent; lines with < are the response. Lines with * are connection notes from curl. This is the single most useful command for understanding why a request behaves a certain way.
Other handy flags:
curl -I https://example.com # headers only (sends a HEAD request)
curl -X POST -d '{"name":"sam"}' \
-H 'Content-Type: application/json' \
https://api.example.com/users # send a POST with a JSON body
curl -L https://example.com # follow redirects automatically
How HTTPS adds TLS encryption
Plain HTTP sends everything as readable text. Anyone between you and the server — your ISP, a coffee-shop Wi-Fi router, an attacker — can read or change it. HTTPS fixes this by wrapping HTTP inside TLS (Transport Layer Security — the modern successor to the older SSL).
TLS does three things:
- Encryption — scrambles the data so eavesdroppers see gibberish.
- Integrity — detects if anyone tampered with the data in transit.
- Authentication — proves the server really is
example.com, using a certificate signed by a trusted authority.
When a client connects, it performs a TLS handshake: it agrees on encryption settings and verifies the server’s certificate before any HTTP data flows. You can see this happening in the * Server certificate: lines of the curl output above. HTTPS runs on port 443 by default; plain HTTP uses port 80.
You should always use HTTPS in 2026 — browsers mark plain HTTP sites as “Not Secure”. Free, auto-renewing certificates are available from Let’s Encrypt via the certbot tool:
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com
This obtains a certificate, edits your Nginx config to enable HTTPS, and sets up automatic renewal via a systemd timer.
A certificate expires (usually after 90 days for Let’s Encrypt). If renewal fails, visitors get scary browser warnings. Check the renewal timer with
sudo systemctl status certbot.timerand test it withsudo certbot renew --dry-run.
Best Practices
- Always serve sites over HTTPS and redirect plain HTTP (port 80) to HTTPS (port 443) with a
301. - Use
curl -vas your first debugging step — it shows the exact request, response code, and headers. - Read the status code first: 4xx points at the request, 5xx points at the server.
- Keep
GETrequests side-effect free; usePOST/PUT/PATCH/DELETEfor changes. - Set
Content-Typecorrectly when sending a body, or APIs may reject your request. - Automate certificate renewal and monitor expiry so HTTPS never silently breaks.
- Check application logs in
/var/log/nginx/error.logwhen you see502or504from a reverse proxy.