Vercel

Public Files Vs Private Server Files

A deployment contains many files, but not every file should become a public URL. Understanding that boundary is essential before you deploy real projects to Vercel. Static assets such as images, CSS, JavaScript bundles, favicons, and downloadable documents may be public. Source files, notes, credentials, database dumps, certificates, test fixtures, and private customer files should not be public.

Beginners often confuse three different ideas: files in the repository, files in a framework's public directory, and files used by server-side code. They are not the same. A repository can contain source code and configuration that Vercel uses during build. A public/ directory is normally intended for browser-accessible assets. Server-side functions can use private values and code paths that should never be sent to the browser.

The safest starting rule is: if a file is in a public asset directory or final static output, assume a browser can request it. If a file contains secrets, private data, or internal notes, it should not be there.

Public Means Browser-Accessible

A public file is a file users can request directly with a URL. In a plain static site, index.html, styles.css, and app.js are public because the browser downloads them. In a Next.js project, the official docs describe the root public folder as a place for static files that can be referenced from the base URL. For example, public/logo.png can be requested as /logo.png.

That behavior is useful for assets:

public/
  logo.png
  favicon.ico
  robots.txt
  downloadable-guide.pdf

But it is dangerous for private files:

public/
  .env
  customers.csv
  database.sql
  private-key.pem
  contract-draft.pdf

Those private examples should not be in public/. If a visitor can guess or find the path, they may be able to download the file. Security through obscure filenames is not security.

Repository Files Are Not All Public URLs

A Git repository often contains many files that are not served directly:

src/
  app code
package.json
README.md
tests/
  fixtures
.env.example

In a framework deployment, Vercel uses the repository to install dependencies, run the build, and produce output. The browser usually receives the built output, not every repository file. That is why src/App.jsx in a Vite project is not normally visited as /src/App.jsx after build. The build process transforms source into browser-ready assets.

However, this does not mean it is safe to commit secrets. Repository files may still be visible to collaborators, Git providers, deployment systems, build logs, local machines, and future maintainers. A file does not need to be a public URL to be a serious leak.

Use this distinction carefully:

Not automatically public URL
  does not mean safe for secrets

Public asset directory
  should contain only browser-safe files

Server-side environment variable
  better place for secrets than Git

Server-Side Files And Code

Full-stack projects can include server-side code. Vercel Functions, framework route handlers, and server-rendered pages can run code that the browser does not download as source. That server-side boundary is where private API keys and privileged provider calls belong.

For example, a contact form should not expose an email provider API key in app.js. Instead:

Browser
  submits form data

Server-side function
  validates form data
  reads EMAIL_API_KEY from environment
  calls email provider

Email provider
  receives authenticated server request

The function source code may be in the repository, but the secret value should be in Vercel Environment Variables. The browser should receive only the response it needs, not the API key.

Server-side files can also read templates, validation schemas, or private configuration that should not be directly downloadable. Whether that is supported depends on the framework and bundling behavior, so test the actual deployment. Do not assume a file is available at runtime just because it exists locally; serverless bundling may include only traced dependencies or build output.

Public Assets In Different Project Types

Different tools use slightly different conventions.

A plain static site may have every deployable file at the project root:

index.html
styles.css
app.js
images/logo.png

A Vite app often uses public/ for files copied as-is and src/ for files bundled into generated output:

public/
  favicon.ico
src/
  main.jsx
  App.jsx

A Next.js app uses public/ for static assets served from the base URL and app or pages directories for routes and code:

public/
  logo.png
app/
  page.tsx
  api/contact/route.ts

The exact framework matters, but the security rule is stable: public asset folders are for files safe to download. Source folders are for code. Secrets are not for either place; they belong in environment variables or an approved secret store.

Private To Users Is Not The Same As Secret

Some files are private from browser users but still reasonable to keep in a repository. Source code, test code, TypeScript configuration, package manifests, and build scripts are not meant to be direct public downloads, but they are normal project files. They can be visible to repository collaborators and deployment systems without being secrets.

Other files are different. A database password, private key, access token, production dump, or customer export should not be committed at all. The question is not only "can the browser download it?" The better question is "who can read this file anywhere in the toolchain?" GitHub, local clones, build systems, code review tools, backup systems, and logs may all handle repository content.

Use three buckets:

Public asset
  safe for any visitor to download

Private project file
  safe for collaborators and build systems, not served directly

Secret or sensitive data
  not committed; stored in environment variables, secret systems, or protected storage

This vocabulary makes reviews clearer. A file can be private to users but still inappropriate for Git if it contains sensitive data.

Files That Should Never Be Public

Some files should never be deployed as public assets:

.env
.env.local
.env.production
service-account.json
private-key.pem
certificate-key.pem
database.sql
customers.csv
backup.zip
client-notes.md
internal-roadmap.pdf
raw-upload-folder/

This list is not complete. Ask what would happen if a stranger downloaded the file. If the answer includes account takeover, customer privacy issues, spam, billing abuse, legal exposure, or embarrassment, the file does not belong in public output.

Do not rely on .vercelignore as the primary secret-management system. Ignoring files can reduce deployment size and avoid shipping unnecessary artifacts, but secrets should not be in the repository in the first place. .vercelignore is covered in the next lesson.

User Uploads Are Not Repository Files

A common beginner mistake is treating the repository as storage for user uploads. That does not work for real applications. A deployment is built from a commit. User uploads happen after deployment and need durable storage such as Vercel Blob, another object storage provider, or a database-backed file system design.

Do not write user uploads into a Git repository and redeploy. That loses data, mixes user content with source control, and creates privacy problems. Do not store uploaded private files in public/ unless they are intentionally public downloads.

A safer model is:

Public marketing image
  committed asset or public folder file

User-uploaded profile image
  object storage with access rules

Private invoice PDF
  private storage, served through authorized server-side route

Application secret
  environment variable or secret manager

Storage design is part of security. The place where a file lives should match who is allowed to read it.

Build Output And Accidental Exposure

Build tools can copy files into output directories. A static site generator might copy everything in public/. A custom script might copy a whole folder. A careless build step might include fixtures or notes that were only meant for local testing.

Before production, inspect the output model:

What directory is served?
Which folders are copied as-is?
Which assets are bundled?
Are source maps generated?
Are test fixtures included?
Are docs or notes included?
Are private files excluded before build?

Source maps deserve special attention. They can be useful for debugging, but production browser source maps may reveal more source detail than intended. Whether to publish them is a project decision that should consider debugging value, code visibility, and security posture.

A Practical File Review

Before deploying a project, do a file review:

Public asset folders contain only browser-safe files.
No .env files are committed.
No real credentials are committed.
No customer data is committed.
No private certificates or keys are committed.
No database dumps or backups are committed.
Generated output contains only intended public files.
Server-side code reads secrets from environment variables.

For client work, add one more check: you have permission to publish every image, PDF, font, logo, and brand asset in the public output. Public does not only mean technically downloadable; it also means legally and commercially ready to distribute.

A Safe File Map For A Small App

Before deployment, draw a quick map of where each category of file belongs. For a small contact form site, the map might look like this:

public/logo.png
  public brand image, safe for browser download

src/App.jsx
  frontend source, bundled into public JavaScript during build

api/contact.ts
  server-side request handler, not downloaded as browser source

.env.local
  local developer values, ignored by Git

EMAIL_API_KEY in Vercel
  server-side secret, scoped to Preview and Production

uploads/
  not committed; user files belong in storage

That map gives reviewers something concrete to check. If a new file appears, ask which category it belongs to. A logo belongs in public output. A form handler belongs in server-side code. A provider key belongs in environment configuration. A customer upload belongs in storage, not in the repository.

The map also helps when debugging. If the browser cannot load a logo, it may belong in a public asset folder or imported through the framework's asset pipeline. If a server-side function cannot read a template, the problem may be build packaging rather than URL access. If a secret is missing, the fix is environment configuration, not moving the secret into public/ so the code can fetch it.

This habit is deliberately simple. Most exposure bugs come from a file being placed in the wrong category and nobody stopping to name the category.

Common Mistakes

The first mistake is putting private files in public/ because they are convenient to reference. Convenience does not change access rules.

The second mistake is committing secrets because the framework does not serve source files directly. Git exposure is still exposure.

The third mistake is assuming a generated URL is obscure enough to hide private files. URLs can be shared, logged, guessed, crawled, or leaked.

The fourth mistake is storing user uploads in the repository. Use real storage with access controls.

The fifth mistake is publishing private source maps or test fixtures without review. Build output needs inspection, not blind trust.

The sixth mistake is believing server-side code makes all nearby files private. Framework bundling and output rules determine what is deployed and what is served.

Review Questions

  • What does it mean for a file to be public in a deployed web app?
  • Why is public/ the wrong place for secrets or private documents?
  • Why are repository files not automatically safe for secrets?
  • What is the right pattern for calling a private API from a browser-facing app?
  • Why should user uploads not be stored in the Git repository?
  • What should you inspect before trusting build output?

Official References