A Git commit is a record of source changes. Nginx serves the deployed files or forwards requests to an application. A CDN may keep copies of responses closer to visitors. These are separate responsibilities, and reliable delivery depends on connecting them deliberately. A successful push does not mean a complete release is live, and a successful origin deployment does not mean every cache has refreshed.

Build a delivery plan that identifies the approved commit, the generated artifact, the active origin release, and the intended cache behavior. The static deployment workflow provides the earlier build and review stages. This guide focuses on the point where those files become HTTP responses.

Separate source, releases, and persistent data

Keep the Git repository outside the public document root. Publish only the intended build output into a release directory that the web server can read. The Nginx worker should not need write permission to source history, deployment credentials, or backup storage. Assign deployment to a separate identity with a defined destination.

Keep persistent uploads and application data outside replaceable release directories. Otherwise a routine activation or cleanup can remove data that was never part of the Git build. For an entirely static site, the public artifact may be sufficient for website delivery. For WordPress, Django, or another dynamic application, document the runtime, database, and shared storage separately.

Decide what each URL should do

Make a route inventory before writing configuration. A content website may have a real HTML file for each article. A browser application may need selected navigation paths to load its application entry page. An API prefix must reach its backend. Downloads need correct file types and error handling. Each case deserves an explicit rule.

A broad fallback that returns the homepage for every unknown path can conceal missing images and broken article links. For an ordinary generated website, return a real not-found response when no corresponding file exists. Use application fallbacks only where the frontend router needs them, and keep asset failures distinguishable from valid pages.

The following fragment illustrates a static directory layout with an explicit not-found outcome. It belongs inside an existing, reviewed server configuration; it is not a complete TLS, domain, or security setup:

root /srv/www/gitfile/current/public;
index index.html;

location / {
    try_files $uri $uri/ =404;
}

Adapt the document root to the actual release layout. If your generator outputs extension-based files instead of directories, design and test that route mapping intentionally. Check a valid nested URL, an invalid URL, and an absent asset after any routing change.

Give HTML and assets different freshness policies

Entry documents usually need updates to become visible sooner than versioned images, stylesheets, and scripts. A content-versioned asset filename changes when its bytes change, allowing an older URL to continue identifying an older file. For example, a build could publish app.f3a9c2.css and update HTML to reference it.

Choose policy according to content behavior. Long cache lifetimes suit public assets whose URLs are never reused for changed bytes. HTML may use a shorter lifetime or require revalidation. The HTTP directive no-cache permits storage but requires successful validation before reuse; no-store instructs caches not to store the response. Neither directive replaces access control.

A CDN has its own rules, so an origin header is only one part of the configuration. Review whether an edge rule overrides origin freshness, whether query parameters enter the cache key, and whether cookies or authorization change eligibility. Avoid assuming that a provider caches every response or interprets every optional directive identically.

Apply response headers with care

Nginx can set caching and other response headers through its HTTP headers module. The official Nginx headers documentation describes the supported directives and their inheritance behavior. Check the installed version before adopting syntax from a newer example.

Header inheritance can surprise maintainers: adding headers in a more specific location can affect which parent-level headers are inherited. Response status also matters. Test representative successful, redirected, and error responses instead of assuming a directive visible in one block appears everywhere. Check the final headers at both the origin and the public hostname.

Avoid stacking overlapping cache settings without checking the result. Multiple layers may emit conflicting instructions or overwrite each other. Keep the intended policy beside the route configuration in plain language: which content it covers, how long stale content is acceptable, and how an urgent correction becomes visible.

Treat personalized responses separately

Public static files and private application responses should not inherit the same cache policy by accident. Identify authentication routes, account pages, administrative interfaces, and APIs that return user-specific data. Configure shared-cache behavior deliberately and test with separate user sessions. A fast response is a failure if it exposes another user's information.

Use backend authorization for protected resources, and review cache keys before allowing any personalized response to be shared. For a mixed application, separate the static asset path from dynamic endpoints. The CMS and framework hub explains why a repository containing PHP or Python files needs more than static file delivery.

Activate releases without mixing versions

Prepare the new artifact in a separate directory and verify it before activation. Use the deployment mechanism appropriate to your filesystem and host to switch the active release as one controlled step. Avoid a sequence that removes the current target before the replacement is ready. Keep the previous release available for recovery.

Nginx configuration changes need their own validation. Run nginx -t through the administrative process before a reload; it checks syntax and attempts to open referenced files. Passing that check does not verify website behavior, certificates from a visitor's perspective, or every backend dependency. Follow it with targeted request checks.

If the deployment only changes the files behind a stable document root, determine whether your server arrangement requires a configuration reload at all. Keep activation scripts small, explicit, and serialized so an older build cannot overwrite a newer release merely because it finished later.

Keep origin and edge checks distinct

Check the origin response through a trusted operational route, then inspect the public domain through the CDN. Compare status, content, caching headers, and any provider-specific cache indicator. A difference helps locate the issue: the origin may be correct while the edge is stale, or the CDN may faithfully deliver an incorrect origin response.

Verify HTTPS and the expected host routing across the complete request path. When an origin is intended to receive traffic only through a proxy, apply the provider's supported origin-protection design and maintain it. Do not trust arbitrary forwarded headers from direct public clients as proof of visitor identity or connection security.

Make invalidation and rollback compatible

Prefer new filenames for changed immutable assets. Use deliberate invalidation for URLs that must retain their names, such as an urgent HTML correction. Purging everything on every deployment creates extra work and can obscure a poor freshness policy. Define when a targeted purge is needed and how to verify that it completed.

Keep older versioned assets reachable at their existing public URLs, through shared asset storage or by including them in the active release. Retaining an old release folder alone is insufficient if all requests resolve only against the current document root. A rollback may reactivate earlier HTML that references earlier scripts. Keep a release manifest or artifact inventory so you know which files belong together. Plan cleanup separately from activation and exclude persistent data from release cleanup.

Tune performance from observed behavior

Measure representative pages before changing compression, cache lifetimes, or server settings. Separate slow origin responses from large assets and browser rendering delays; they call for different fixes. Compare a first request with a repeated request, and record the response size and cache outcome alongside timing.

Use the same small sample after a change so comparisons remain meaningful. Include a page with typical images and scripts rather than only an unusually small homepage. Keep correctness checks in the review, because a faster cached response is useful only when it contains the intended content.

Common mistakes

Frequent problems include using the repository checkout as the document root, giving mutable assets long-lived URLs, caching private responses under broad rules, and returning success for missing files. Another is checking only the homepage after a change. Include a nested route, an asset, a deliberate error, and a dynamic endpoint when one exists.

Frequently asked questions

Does adding a CDN remove the origin?

Usually the origin remains responsible for responses the CDN must fetch or revalidate. Some hosting products combine these roles, but you still need to understand where the authoritative release lives.

Should every response be cached?

No. Cache eligibility should follow the data and access requirements. Public versioned assets are straightforward; authenticated or changing responses need more deliberate rules.

Can Nginx serve Django or WordPress by itself?

Nginx can serve their static assets and forward application requests, but the relevant application runtime and data services still need to operate. A static export is a different publication model.

Deliver a release you can identify

Keep a clear record from approved commit to artifact to active origin release, then verify what the public edge serves. That record makes performance work, urgent fixes, and rollback easier to reason about because each layer has a defined responsibility.