Private Web Apps

How to Put a Private Static Website Behind a VPN

Protect documentation, prototypes, client portals, and internal static sites with a private VPN route and a deliberately closed origin.

Featured image for How to Put a Private Static Website Behind a VPN

A static website is often described as simple and low risk because it has no database or server-side application code. That simplicity is valuable, but it does not make every static site suitable for public hosting. Internal documentation, design prototypes, customer deliverables, build reports, incident runbooks, and pre-release product pages can contain sensitive information even when they are only HTML, CSS, JavaScript, and images. A Site VPN lets authorized devices reach that content while the origin remains unavailable to general internet traffic.

The safest pattern is not “publish it normally and hide the URL.” It is a private network path, a web server bound to a private interface, HTTPS, and a deployment pipeline that does not accidentally copy confidential files to a public bucket or CDN. This guide explains how to make those boundaries explicit and how to avoid browser caching, DNS, and build-process surprises that can outlive the VPN session.

Decide whether the content truly belongs behind a VPN

A VPN is appropriate when the audience is limited and devices can be enrolled. Examples include an employee handbook, a customer-specific project portal, a staging build, or an offline-style knowledge base for a field team. It is less appropriate for a public marketing site that merely needs an administrative area protected. In that case, keep the public pages public and move the management interface to a separate private hostname or network. Mixing audiences on one origin increases the chance of a routing or caching mistake.

Classify the material before deployment. Identify whether it contains personal data, credentials, internal hostnames, source maps, API endpoints, financial information, or unreleased product details. A static generator can include files that were never linked from a page, and directory indexes or predictable filenames can expose them. The VPN reduces who can connect, but the build should still contain only what the authorized audience needs. Remove secrets entirely; do not treat private network access as permission to embed passwords or long-lived tokens in JavaScript.

Use a private origin, not an obscure public URL

Place the generated site on a server that listens only on a WireGuard address, a loopback address behind a reverse proxy bound to that interface, or a private network reachable from the VPN gateway. Confirm the process is not listening on the VPS public address or IPv6 wildcard. Cloud firewalls are useful, but host-level binding and firewall rules provide a second boundary. Test the public address directly, with the expected hostname and with an alternate host header, to make sure the private content does not appear.

A directory path such as public.example.com/secret-project/ is not a network boundary. Search engines, referrer headers, browser history sync, access logs, and shared links can reveal it. HTTP basic authentication alone can be useful as an additional control, but it still exposes the server and login surface to the internet. The Site VPN should determine whether a packet can reach the origin; application or web-server authentication can then determine which authorized user may view a particular collection.

Keep remote-access infrastructure hardened

A private site inherits the security of its gateway. Patch the operating system and VPN software, restrict management access, use unique device keys, and remove inactive peers. CISA’s Guide to Securing Remote Access Software emphasizes inventory, secure configuration, logging, and monitoring around remote access. Apply those principles even to a small documentation site: know which service is exposed, who administers it, and how a compromised or departed device is revoked.

Do not let a single VPN peer grant access to every service on the server. Use a dedicated private subnet, firewall by interface and destination port, and run the web server with minimal privileges. If the same VPS hosts SSH, databases, or other applications, separate them with firewall rules or containers and avoid publishing broad routes to clients. The static site may be harmless, but a route to its host can become an unintended path to more sensitive services.

Plan private DNS and HTTPS together

Give the site a stable hostname such as docs.private.example.com. A private resolver can return the WireGuard address only to connected clients, while public DNS returns no record. Another pattern uses a public DNS record containing a private address; this does not expose the content, but it reveals naming and addressing and may behave differently on networks that filter private answers. Pick one pattern, document it, and ensure all supported devices resolve the same way.

Use HTTPS even though WireGuard already encrypts the network path. TLS gives the browser an independent identity check, protects against mistakes inside the private network, and preserves secure-context features. DNS-based certificate validation can issue a publicly trusted certificate without opening the origin. A private certificate authority can work for managed devices but requires trust distribution. Avoid self-signed warnings in normal operation; users trained to ignore warnings cannot reliably identify a real interception or wrong destination.

Design the deployment pipeline to prevent public leakage

Separate public and private build targets. Use different output directories, storage destinations, deployment credentials, and ideally different jobs. A single “deploy” script with an environment flag can be convenient but dangerous if the default points to a public bucket. Make the private destination explicit and require a review for changes to hosting configuration. Scan generated output for secret patterns, source maps, environment files, and content marked confidential before uploading.

Store the source repository according to its sensitivity. Private hosting does not help if the repository is public or if build artifacts are attached to a public release. Limit CI logs because static generators may print file paths, environment values, or deployment URLs. Use short-lived deployment credentials and restrict them to the private origin. Record the build commit and deployment time in a non-sensitive footer or manifest so administrators can verify which version they are viewing.

Control browser caches and service workers

Static sites are designed to cache well, but confidential content on a shared or lost device can remain after the VPN disconnects. Set cache headers according to the threat model. Highly sensitive pages may use Cache-Control: no-store; less sensitive documentation may allow private caching with a short lifetime. Remember that images, PDFs, and downloadable archives need their own headers. Browser history, screenshots, printouts, and downloads are outside the VPN and require policy or device controls.

Be especially careful with service workers and offline features. A service worker can intentionally store the whole site and continue serving it after the network route disappears. That may be valuable for field work, but it changes the security model from “available only through the VPN” to “available to anyone who can use this browser profile.” If offline access is not required, do not register a service worker. If it is required, use managed devices, local encryption, clear revocation expectations, and an explicit content-expiration strategy.

Add application identity only when the use case needs it

For a small team, unique VPN peers plus device login may be sufficient to view low-risk internal documentation. For client portals, regulated content, or shared devices, add named user authentication at the reverse proxy or application layer. This produces user-level audit events and allows access to change without replacing a device profile. Keep the login service private as well, and use multifactor authentication when the consequence of account compromise is significant.

Do not add a complex content management system merely to obtain logins if the content can remain static. A reverse proxy can enforce identity before serving files, or the build can create separate sites on distinct private hostnames with different network policies. Simpler publishing reduces patching and database risk. The goal is the minimum system that provides the required audience control, auditability, and editing workflow.

Test both access and non-access

From an enrolled device, connect the tunnel, resolve the hostname, verify the certificate, load representative pages and assets, and confirm downloads use the intended cache policy. Then disconnect and repeat. Test from a device that has never been enrolled, from the VPS public address, over IPv6, and through alternate ports. Inspect storage or CDN dashboards to ensure the private build is not present in a public location. Search the public site repository and sitemap for private URLs.

Revocation testing matters too. Remove a pilot peer and verify it can no longer handshake or load cached protected content beyond the chosen policy. Rotate the site certificate and confirm clients recover. Rebuild the origin from backup to prove the deployment is reproducible. A private static site should be boring to operate: a known artifact goes to a known private destination, reachable only through a documented path.

Conclusion

A static site behind a Site VPN can deliver private documentation and web content with a small attack surface, but only when the origin, DNS, TLS, build pipeline, and browser caching rules all reinforce the same boundary.

Keep secrets out of the build, bind the server to a private interface, test from an unapproved network, and decide deliberately whether offline caching is allowed. The absence of a database simplifies the system; it does not remove the need for access design.

Keep reading

Related SiteVPN guides

Continue with adjacent architecture, deployment, and private-web topics.