<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Website delivery foundations on StackOnward</title><link>https://stackonward.com/series/website-delivery-foundations/</link><description>Recent content in Website delivery foundations on StackOnward</description><generator>Hugo</generator><language>en-US</language><atom:link href="https://stackonward.com/series/website-delivery-foundations/index.xml" rel="self" type="application/rss+xml"/><item><title>How to Troubleshoot a Slow Website: DNS, TTFB, and LCP</title><link>https://stackonward.com/posts/slow-website-troubleshooting/</link><pubDate>Fri, 11 Sep 2026 05:18:00 +0800</pubDate><guid>https://stackonward.com/posts/slow-website-troubleshooting/</guid><description>&lt;p&gt;How to troubleshoot a slow website starts with the URL, the time window, and who is affected. Split one load into DNS, connection, TLS, waiting for the first byte, body download, and rendering. Hosting, CDN, database, and frontend changes only help after that interval has an owner.&lt;/p&gt;
&lt;figure&gt;
 &lt;img src="https://stackonward.com/images/posts/slow-website-troubleshooting/en/performance-path.svg" alt="Performance path from confirming URL and time, through curl phase timings, then to CDN and origin when TTFB is high or to browser rendering when HTML is already fast" loading="lazy" decoding="async"&gt;
 &lt;figcaption&gt;curl locates network and first-byte time. Origin metrics and the browser timeline explain the remaining wait.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="start-troubleshooting-a-slow-website-from-the-affected-url"&gt;Start troubleshooting a slow website from the affected URL&lt;/h2&gt;
&lt;p&gt;Record the incident before opening a profiler:&lt;/p&gt;</description></item><item><title>What Happens When You Type a URL in the Browser</title><link>https://stackonward.com/posts/domain-request-flow/</link><pubDate>Fri, 11 Sep 2026 05:18:00 +0800</pubDate><guid>https://stackonward.com/posts/domain-request-flow/</guid><description>&lt;p&gt;What happens when you type a URL in the browser is a sequence of hops, not a round trip to one origin. The request usually travels through DNS, a TCP or QUIC connection, TLS, a CDN or reverse proxy, and only then the application. The HTML that comes back is not the end; the browser still fetches styles, scripts, fonts, and images. &lt;a href="https://developer.mozilla.org/en-US/docs/Learn_web_development/Getting_started/Web_standards/How_the_web_works" target="_blank" rel="noopener"&gt;MDN’s account of how the web works&lt;span class="sr-only"&gt;(opens in a new tab)&lt;/span&gt;&lt;/a&gt; lists DNS, the HTTP request, the server response, and the browser assembling the page as consecutive stages.&lt;/p&gt;</description></item><item><title>Caddy vs Nginx vs Nginx Proxy Manager</title><link>https://stackonward.com/posts/caddy-nginx-npm-comparison/</link><pubDate>Fri, 11 Sep 2026 05:15:00 +0800</pubDate><guid>https://stackonward.com/posts/caddy-nginx-npm-comparison/</guid><description>&lt;p&gt;Caddy vs Nginx is decided by where the reverse-proxy config lives and how certificates are renewed, not by a public request-per-second ranking. A small site with files in Git and default automatic HTTPS usually belongs on Caddy. Working Nginx configs, modules, or operator muscle stay on Nginx. A web UI for a handful of hosts is Nginx Proxy Manager, and that choice also means backing up an application, a database, and generated Nginx files.&lt;/p&gt;
&lt;figure&gt;
 &lt;img src="https://stackonward.com/images/posts/caddy-nginx-npm-comparison/en/proxy-decision-tree.svg" alt="Decision tree that chooses Caddy, Nginx, or Nginx Proxy Manager from file versus web UI maintenance, then from existing Nginx assets" loading="lazy" decoding="async"&gt;
 &lt;figcaption&gt;Existing Nginx assets stay on the file path. A GUI path still needs a backup of the admin data.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="caddy-vs-nginx-is-decided-by-the-config-source-of-truth"&gt;Caddy vs Nginx is decided by the config source of truth&lt;/h2&gt;
&lt;div class="table-responsive" role="region" aria-label="Scrollable table" tabindex="0"&gt;
 &lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Axis&lt;/th&gt;
					&lt;th&gt;Caddy&lt;/th&gt;
					&lt;th&gt;Nginx&lt;/th&gt;
					&lt;th&gt;Nginx Proxy Manager&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Daily config&lt;/td&gt;
					&lt;td&gt;Caddyfile or JSON&lt;/td&gt;
					&lt;td&gt;Nginx config files&lt;/td&gt;
					&lt;td&gt;Web admin UI&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;HTTPS&lt;/td&gt;
					&lt;td&gt;Certificate issue and renewal are default&lt;/td&gt;
					&lt;td&gt;Usually Certbot, acme.sh, or a cloud certificate&lt;/td&gt;
					&lt;td&gt;Common certificate actions in the UI&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Git&lt;/td&gt;
					&lt;td&gt;Reviewed as files&lt;/td&gt;
					&lt;td&gt;Reviewed as files&lt;/td&gt;
					&lt;td&gt;Main state lives in a database&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;WebSocket&lt;/td&gt;
					&lt;td&gt;HTTP reverse proxy handles the usual upgrade&lt;/td&gt;
					&lt;td&gt;Upgrade-related headers must be set correctly&lt;/td&gt;
					&lt;td&gt;Follows the generated Nginx config&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Cache and limits&lt;/td&gt;
					&lt;td&gt;Configurable&lt;/td&gt;
					&lt;td&gt;Mature and fine-grained&lt;/td&gt;
					&lt;td&gt;Bounded by the UI and Advanced config&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Extra state&lt;/td&gt;
					&lt;td&gt;Config plus certificate storage&lt;/td&gt;
					&lt;td&gt;Config, certificates, and related scripts&lt;/td&gt;
					&lt;td&gt;App, database, certificates, and generated config&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;

&lt;/div&gt;

&lt;p&gt;Files are easy to review and roll back. A database is easy to click through, and a restore has to cover that database together with the certificates.&lt;/p&gt;</description></item><item><title>Docker Compose Production: Images, Health Checks, Secrets</title><link>https://stackonward.com/posts/docker-compose-production/</link><pubDate>Fri, 11 Sep 2026 05:12:00 +0800</pubDate><guid>https://stackonward.com/posts/docker-compose-production/</guid><description>&lt;p&gt;Docker Compose production on one host is a pinned image, durable data, a private app port, resource caps, and a stack that returns after a container or host restart. &lt;code&gt;docker compose up -d&lt;/code&gt; only reports that the containers started.&lt;/p&gt;
&lt;figure&gt;
 &lt;img src="https://stackonward.com/images/posts/docker-compose-production/en/compose-topology.svg" alt="Single-host Docker Compose production with a reverse proxy, app container, persistent volume, secret file, and size-capped logs" loading="lazy" decoding="async"&gt;
 &lt;figcaption&gt;The public internet should reach only the reverse proxy on 80/443. Keep the app port on loopback or on the container network.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="when-docker-compose-production-is-the-right-fit"&gt;When Docker Compose production is the right fit&lt;/h2&gt;
&lt;p&gt;Compose can run production when the whole stack lives on one machine, the service graph is small, and a host failure is recovered by a person or a script on another machine:&lt;/p&gt;</description></item><item><title>How to Configure Docker Volume Permissions and Secrets</title><link>https://stackonward.com/posts/docker-volume-permissions-secrets/</link><pubDate>Fri, 11 Sep 2026 05:12:00 +0800</pubDate><guid>https://stackonward.com/posts/docker-volume-permissions-secrets/</guid><description>&lt;p&gt;How to configure Docker volume permissions starts with a numeric UID and GID match, not a matching username. Put database files and other container-generated state on a named volume, bind-mount host-managed config read-only, and grant each secret only to the service that reads it as a file. Recreate the container to prove the mount. Restore a backup in a separate environment to prove the backup.&lt;/p&gt;
&lt;figure&gt;
 &lt;img src="https://stackonward.com/images/posts/docker-volume-permissions-secrets/en/storage-boundaries.svg" alt="An application container writes database state to a named volume, reads host config read-only, and reads a password from a granted secret file" loading="lazy" decoding="async"&gt;
 &lt;figcaption&gt;Named volumes hold writable state. Bind mounts hold host-managed config. Secret files are granted per service.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="choose-a-named-volume-bind-mount-or-tmpfs"&gt;Choose a named volume, bind mount, or tmpfs&lt;/h2&gt;
&lt;div class="table-responsive" role="region" aria-label="Scrollable table" tabindex="0"&gt;
 &lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Type&lt;/th&gt;
					&lt;th&gt;What belongs there&lt;/th&gt;
					&lt;th&gt;Why&lt;/th&gt;
					&lt;th&gt;Main risk&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Named volume&lt;/td&gt;
					&lt;td&gt;Databases, queues, application state&lt;/td&gt;
					&lt;td&gt;Docker manages the lifecycle; the path is not tied to one host layout&lt;/td&gt;
					&lt;td&gt;Host backup and migration must name the volume and its driver&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Bind mount&lt;/td&gt;
					&lt;td&gt;Caddyfile, explicit data directories, host scripts&lt;/td&gt;
					&lt;td&gt;The path is visible to existing backup and permission tools&lt;/td&gt;
					&lt;td&gt;The host path, owner, and SELinux or AppArmor labels all have to match&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;tmpfs&lt;/td&gt;
					&lt;td&gt;Short-lived sensitive files or fast scratch data that must not persist&lt;/td&gt;
					&lt;td&gt;Nothing is written to durable disk&lt;/td&gt;
					&lt;td&gt;A restart drops the files; the size counts against container memory&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/engine/storage/" target="_blank" rel="noopener"&gt;Docker storage&lt;span class="sr-only"&gt;(opens in a new tab)&lt;/span&gt;&lt;/a&gt; treats &lt;a href="https://docs.docker.com/engine/storage/volumes/" target="_blank" rel="noopener"&gt;volumes&lt;span class="sr-only"&gt;(opens in a new tab)&lt;/span&gt;&lt;/a&gt; as the default for data a container generates. &lt;a href="https://docs.docker.com/engine/storage/bind-mounts/" target="_blank" rel="noopener"&gt;Bind mounts&lt;span class="sr-only"&gt;(opens in a new tab)&lt;/span&gt;&lt;/a&gt; are for files the host already owns. &lt;a href="https://docs.docker.com/engine/storage/tmpfs/" target="_blank" rel="noopener"&gt;tmpfs&lt;span class="sr-only"&gt;(opens in a new tab)&lt;/span&gt;&lt;/a&gt; stays in host memory and is gone when the container stops. The image writable layer is the wrong place for runtime data: deleting the container deletes that layer.&lt;/p&gt;</description></item><item><title>How to Troubleshoot 502 and 504 Behind a Reverse Proxy</title><link>https://stackonward.com/posts/502-504-troubleshooting/</link><pubDate>Fri, 11 Sep 2026 05:08:00 +0800</pubDate><guid>https://stackonward.com/posts/502-504-troubleshooting/</guid><description>&lt;p&gt;How to troubleshoot 502 and 504 starts at the hop that produced the status. Both codes come from a gateway or reverse proxy: 502 Bad Gateway means that hop did not get a usable response from upstream; 504 Gateway Timeout means it waited past its limit. Keep the headers, then reach the upstream from that hop&amp;rsquo;s network.&lt;/p&gt;
&lt;figure&gt;
 &lt;img src="https://stackonward.com/images/posts/502-504-troubleshooting/en/gateway-failure-map.svg" alt="A client reaches an app through a CDN and reverse proxy; a failed connection becomes 502, a wait past the limit becomes 504" loading="lazy" decoding="async"&gt;
 &lt;figcaption&gt;Connection refused, DNS failure, and an invalid response usually point to 502. An upstream wait past the proxy deadline usually points to 504.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="how-to-troubleshoot-502-and-504-at-the-hop-that-returned-the-status"&gt;How to troubleshoot 502 and 504 at the hop that returned the status&lt;/h2&gt;
&lt;p&gt;A browser may sit behind Cloudflare or another CDN, a cloud load balancer, and Caddy or Nginx. Save the status, headers, time, and request ID:&lt;/p&gt;</description></item><item><title>Automatic HTTPS Certificate Renewal with Caddy and ACME</title><link>https://stackonward.com/posts/https-certificate-automatic-renewal/</link><pubDate>Fri, 11 Sep 2026 05:05:00 +0800</pubDate><guid>https://stackonward.com/posts/https-certificate-automatic-renewal/</guid><description>&lt;p&gt;Automatic HTTPS certificate renewal in Caddy is not a cron job. Caddy obtains a certificate, serves HTTPS, redirects HTTP, and renews in the background when the hostname, public DNS, ACME challenge path, and certificate storage all work. A running Caddy process does not prove the next ACME attempt will succeed.&lt;/p&gt;
&lt;figure&gt;
 &lt;img src="https://stackonward.com/images/posts/https-certificate-automatic-renewal/en/acme-renewal-flow.svg" alt="Flow from Caddy reading a hostname, completing ACME validation, storing the certificate, renewing in the background, and checking TLS from outside" loading="lazy" decoding="async"&gt;
 &lt;figcaption&gt;After issuance, the certificate, private key, and ACME account state are written to the data directory. Background renewal uses the same challenge path.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="automatic-https-certificate-renewal-only-starts-when-these-conditions-hold"&gt;Automatic HTTPS certificate renewal only starts when these conditions hold&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://caddyserver.com/docs/automatic-https" target="_blank" rel="noopener"&gt;Caddy&amp;rsquo;s Automatic HTTPS documentation&lt;span class="sr-only"&gt;(opens in a new tab)&lt;/span&gt;&lt;/a&gt; requires all of the following at once:&lt;/p&gt;</description></item><item><title>Migrate a Website to a New Domain: 301, 308, Canonical</title><link>https://stackonward.com/posts/website-domain-migration/</link><pubDate>Fri, 11 Sep 2026 04:22:00 +0800</pubDate><guid>https://stackonward.com/posts/website-domain-migration/</guid><description>&lt;p&gt;When you migrate a website to a new domain, each live old URL should take one permanent hop to the matching new URL, including the path and any query string that still belongs to the page. Canonical tags, internal links, sitemaps, structured data, and analytics on the new host must already name the new domain.&lt;/p&gt;
&lt;figure&gt;
 &lt;img src="https://stackonward.com/images/posts/website-domain-migration/en/domain-migration-map.svg" alt="Old-host home, article, topic, and asset URLs each taking one permanent redirect to the same path on the new host" loading="lazy" decoding="async"&gt;
 &lt;figcaption&gt;Home, article, topic, and asset each keep their path on the new host.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="build-a-one-to-one-url-map-before-the-cutover"&gt;Build a one-to-one URL map before the cutover&lt;/h2&gt;
&lt;p&gt;Export old URLs from the sitemap, access logs, Search Console, backlinks, and the database. Then give each address one destination:&lt;/p&gt;</description></item><item><title>Cache-Control for Static Assets: ETag and Fingerprints</title><link>https://stackonward.com/posts/static-asset-cache-control/</link><pubDate>Fri, 11 Sep 2026 04:20:00 +0800</pubDate><guid>https://stackonward.com/posts/static-asset-cache-control/</guid><description>&lt;p&gt;Cache-Control for static assets cannot be one header copied onto every path. Fingerprinted CSS, JavaScript, and fonts can stay cached for a year. HTML has to revalidate so it can pick up new URLs. Images that keep the same filename can stay cached only for as long as outdated bytes are acceptable. Responses that carry account or payment data must not enter a shared cache.&lt;/p&gt;
&lt;figure&gt;
 &lt;img src="https://stackonward.com/images/posts/static-asset-cache-control/en/cache-policy-map.svg" alt="Decision map from public versus sensitive responses to no-store, one-year immutable cache, or short cache and revalidation" loading="lazy" decoding="async"&gt;
 &lt;figcaption&gt;Cache lifetime follows whether the URL changes with the content, whether the response is public, and how long stale copies are allowed to remain.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="cache-control-for-static-assets-starts-from-whether-the-url-changes"&gt;Cache-Control for static assets starts from whether the URL changes&lt;/h2&gt;
&lt;div class="table-responsive" role="region" aria-label="Scrollable table" tabindex="0"&gt;
 &lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Resource&lt;/th&gt;
					&lt;th&gt;When the URL changes&lt;/th&gt;
					&lt;th&gt;Usual policy&lt;/th&gt;
					&lt;th&gt;Publish requirement&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;HTML&lt;/td&gt;
					&lt;td&gt;The address stays; the document changes&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;no-cache&lt;/code&gt; or a short &lt;code&gt;max-age&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Revalidate or expire in time for the next release&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Fingerprinted CSS, JS, and fonts&lt;/td&gt;
					&lt;td&gt;The filename changes with the bytes&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;public, max-age=31536000, immutable&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;HTML must reference the new URL&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Unversioned images and PDFs&lt;/td&gt;
					&lt;td&gt;The same address may be overwritten&lt;/td&gt;
					&lt;td&gt;Short cache plus ETag&lt;/td&gt;
					&lt;td&gt;Purge that URL or change the URL after a replace&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Public API responses&lt;/td&gt;
					&lt;td&gt;Business semantics decide&lt;/td&gt;
					&lt;td&gt;Explicit &lt;code&gt;public&lt;/code&gt; or &lt;code&gt;private&lt;/code&gt;, &lt;code&gt;s-maxage&lt;/code&gt;, and &lt;code&gt;Vary&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The cache key must cover every response difference&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Sensitive or one-time responses&lt;/td&gt;
					&lt;td&gt;Reuse is not allowed&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;no-store&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The CDN must not cache them either&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;app.4f81c9a2.js&lt;/code&gt; can be treated as immutable only because a content change produces a new filename. A directory named &lt;code&gt;/assets/&lt;/code&gt; does not prove that every file inside it is immutable.&lt;/p&gt;</description></item><item><title>How to Expose Docker Ports: 127.0.0.1, 0.0.0.0, Firewalls</title><link>https://stackonward.com/posts/docker-port-firewall-loopback/</link><pubDate>Fri, 11 Sep 2026 04:14:00 +0800</pubDate><guid>https://stackonward.com/posts/docker-port-firewall-loopback/</guid><description>&lt;p&gt;How to expose Docker ports depends on the process listen address inside the container, the host publish bind, and the firewall or cloud security group in front of the host. Listening on &lt;code&gt;0.0.0.0&lt;/code&gt; inside a container does not put that port on the public internet.&lt;/p&gt;
&lt;figure&gt;
 &lt;img src="https://stackonward.com/images/posts/docker-port-firewall-loopback/en/network-boundaries.svg" alt="Public traffic crosses a cloud security group and host firewall to a reverse proxy; the app and database talk only on the Docker network" loading="lazy" decoding="async"&gt;
 &lt;figcaption&gt;The app and database stay on the Docker network. The public internet reaches only the ports the proxy publishes.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="how-to-expose-docker-ports-at-each-network-layer"&gt;How to expose Docker ports at each network layer&lt;/h2&gt;
&lt;div class="table-responsive" role="region" aria-label="Scrollable table" tabindex="0"&gt;
 &lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Where it is set&lt;/th&gt;
					&lt;th&gt;Example&lt;/th&gt;
					&lt;th&gt;What it means&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;App process listen&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;0.0.0.0:8080&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Accept 8080 on every interface in this container&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;App process listen&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;127.0.0.1:8080&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Accept 8080 only on this container’s loopback&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Docker port publish&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;127.0.0.1:8080:8080&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Host-local processes can reach container 8080 on host 8080&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Docker port publish&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;0.0.0.0:8080:8080&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Every IPv4 address on the host publishes 8080; the public internet may reach it&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Compose internal access&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;http://app:8080&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;A peer on the same network uses the service name and the container port&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;

&lt;/div&gt;

&lt;p&gt;The two uses of &lt;code&gt;0.0.0.0&lt;/code&gt; are not the same action. Listening on every interface inside the container lets a proxy on the same Docker network connect. Publishing the port onto every host interface widens the host entry.&lt;/p&gt;</description></item><item><title>DNS Record Types: A, AAAA, CNAME, TXT, and CAA</title><link>https://stackonward.com/posts/dns-record-types/</link><pubDate>Fri, 11 Sep 2026 04:12:00 +0800</pubDate><guid>https://stackonward.com/posts/dns-record-types/</guid><description>&lt;p&gt;DNS record types A, AAAA, CNAME, TXT, and CAA store different answers. A and AAAA hold the server address. CNAME holds another name so a CDN or host can keep the later addresses. TXT holds ownership proofs and mail policy. CAA names which certificate authorities may issue.&lt;/p&gt;
&lt;p&gt;When the hostname should reach a server you control, publish A or AAAA. When the platform issues a hostname instead of a stable IP, publish CNAME. TXT and CAA do not send traffic; they only constrain verification and issuance.&lt;/p&gt;
&lt;figure&gt;
 &lt;img src="https://stackonward.com/images/posts/dns-record-types/en/dns-record-map.svg" alt="How DNS record types A, AAAA, CNAME, TXT, and CAA split between traffic routing and stored policy" loading="lazy" decoding="async"&gt;
 &lt;figcaption&gt;A, AAAA, and CNAME decide where traffic goes. TXT and CAA store verification, policy, or certificate limits.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="how-dns-record-types-a-aaaa-cname-txt-and-caa-differ"&gt;How DNS record types A, AAAA, CNAME, TXT, and CAA differ&lt;/h2&gt;
&lt;div class="table-responsive" role="region" aria-label="Scrollable table" tabindex="0"&gt;
 &lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Record&lt;/th&gt;
					&lt;th&gt;Value&lt;/th&gt;
					&lt;th&gt;Common job&lt;/th&gt;
					&lt;th&gt;Usual failure&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;A&lt;/td&gt;
					&lt;td&gt;IPv4 address&lt;/td&gt;
					&lt;td&gt;Point a name at an IPv4 server&lt;/td&gt;
					&lt;td&gt;The server IP changed and the record did not&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;AAAA&lt;/td&gt;
					&lt;td&gt;IPv6 address&lt;/td&gt;
					&lt;td&gt;Point a name at an IPv6 server&lt;/td&gt;
					&lt;td&gt;IPv6-capable networks try an address the service cannot serve&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;CNAME&lt;/td&gt;
					&lt;td&gt;Another name&lt;/td&gt;
					&lt;td&gt;CDN, hosted platform, object storage&lt;/td&gt;
					&lt;td&gt;The same owner also has A, TXT, or MX&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;TXT&lt;/td&gt;
					&lt;td&gt;A text string&lt;/td&gt;
					&lt;td&gt;Domain ownership, SPF, DKIM, DMARC&lt;/td&gt;
					&lt;td&gt;The console appended the zone, or the copied value gained a space&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;CAA&lt;/td&gt;
					&lt;td&gt;A CA identifier&lt;/td&gt;
					&lt;td&gt;Limit which issuers may mint certificates&lt;/td&gt;
					&lt;td&gt;The CA already in use is missing, so renewal fails&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;

&lt;/div&gt;

&lt;p&gt;Consoles label the DNS name as Host, Name, or Record name. Each record still has a name, a type, a value, and a TTL. &lt;a href="https://docs.cloud.google.com/dns/docs/records-overview" target="_blank" rel="noopener"&gt;Google Cloud DNS records overview&lt;span class="sr-only"&gt;(opens in a new tab)&lt;/span&gt;&lt;/a&gt; lists common uses. A and CNAME semantics are in &lt;a href="https://www.rfc-editor.org/rfc/rfc1034" target="_blank" rel="noopener"&gt;RFC 1034&lt;span class="sr-only"&gt;(opens in a new tab)&lt;/span&gt;&lt;/a&gt;. AAAA is &lt;a href="https://www.rfc-editor.org/rfc/rfc3596" target="_blank" rel="noopener"&gt;RFC 3596&lt;span class="sr-only"&gt;(opens in a new tab)&lt;/span&gt;&lt;/a&gt;. CAA is &lt;a href="https://www.rfc-editor.org/rfc/rfc8659" target="_blank" rel="noopener"&gt;RFC 8659&lt;span class="sr-only"&gt;(opens in a new tab)&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;</description></item></channel></rss>