# How Static Assets (Images, Fonts, etc.) Are Managed in Open SEO's Public Directory

> Learn how Open SEO manages static assets like images and fonts. Discover how the public folder serves unprocessed files directly via Cloudflare Workers for efficient caching.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: internals
- Published: 2026-08-16

---

**Open SEO uses the `public` folder as an unprocessed, verbatim copy of static assets that Cloudflare Workers serve directly to browsers with efficient caching headers.**

The `every-app/open-seo` repository follows Vite's standard convention for static asset handling. Files placed in the `public` directory bypass the build pipeline entirely and are deployed unchanged, making them instantly available via root-relative URLs.

## What the Public Directory Does in Open SEO

The `public` folder serves as a **drop-and-serve** location for any file that should reach the browser exactly as authored. This includes logos, favicons, font files, SVG icons, and other immutable assets.

According to the source code in [`public/`](https://github.com/every-app/open-seo/blob/main/public), the directory contains assets like `transparent-logo.png` and `favicon.ico` that require no transformation. Vite's default `publicDir: "public"` configuration ensures these files are excluded from bundling, hashing, or code splitting.

## How Assets Move from Public to Production

The deployment flow for static assets in Open SEO follows three stages:

1. **Placement** — Developers add files to `public/` using any folder structure (e.g., `public/fonts/`, `public/images/`).

2. **Build-time copy** — During `wrangler deploy` or Vite's build step, the entire `public/` tree is copied verbatim into the Worker asset bundle. No file contents are modified.

3. **Automatic serving** — Cloudflare Workers, configured via [`wrangler.jsonc`](https://github.com/every-app/open-seo/blob/main/badseo/wrangler.jsonc), routes incoming HTTP requests to matching files in the bundled assets without custom handler code.

This means a request for `/favicon.ico` resolves directly to `public/favicon.ico` with no additional routing logic required.

## Referencing Public Assets in Code

Open SEO components use **root-relative paths** that mirror the `public/` folder structure. The browser requests these URLs directly from the Worker.

### Images in React Components

```tsx
export const Logo = () => (
  <img src="/transparent-logo.png" alt="Open SEO" width={120} />
);

```

The `src="/transparent-logo.png"` path corresponds exactly to [`public/transparent-logo.png`](https://github.com/every-app/open-seo/blob/main/public/transparent-logo.png) in the repository.

### Fonts in CSS

```css
@font-face {
  font-family: 'OpenSans';
  src: url('/fonts/OpenSans-Regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
}

```

Place font files in `public/fonts/` and reference them with leading slashes to ensure correct resolution.

### Favicons and Static Files

```html
<link rel="icon" href="/favicon.ico" />

```

No import statements or build-time processing applies—the file is served byte-for-byte as stored in `public/`.

## Caching Behavior for Static Assets

Open SEO configures Cloudflare Workers to serve public assets with aggressive caching headers. The static-asset response includes:

```

Cache-Control: public, max-age=86400, immutable

```

This header pattern, derived from Vite's default route configuration for scripts, applies equally to files copied from `public/`. The `immutable` directive tells browsers these files will never change at their URL, allowing indefinite caching without revalidation requests.

## Key Configuration Files

Understanding these files clarifies how the public directory integrates with Open SEO's deployment:

| File | Role |
|------|------|
| [`public/transparent-logo.png`](https://github.com/every-app/open-seo/blob/main/public/transparent-logo.png) | Example image served unchanged |
| [`public/favicon.ico`](https://github.com/every-app/open-seo/blob/main/public/favicon.ico) | Standard favicon asset |
| [`wrangler.jsonc`](https://github.com/every-app/open-seo/blob/main/badseo/wrangler.jsonc) | Cloudflare Workers config that bundles `public/` as static assets |
| [`vite.config.ts`](https://github.com/every-app/open-seo/blob/main/vite.config.ts) | Implicit `publicDir: "public"` setting; no customization required |

## Summary

- **Unprocessed placement**: Files in `public/` are copied verbatim, never bundled or hashed by Vite.
- **Direct serving**: Cloudflare Workers automatically map URL paths to `public/` files via `wrangler.jsonc` configuration.
- **Root-relative URLs**: Always reference assets with leading slashes (e.g., `/logo.png`).
- **Immutable caching**: 24-hour `max-age` with `immutable` directive maximizes browser caching efficiency.
- **Zero handler code**: No explicit TypeScript routing required for static asset requests.

## Frequently Asked Questions

### Can I use subdirectories inside the public folder?

Yes. Create any folder structure needed—`public/fonts/`, `public/images/`, `public/icons/`—and reference assets with the full path: `/fonts/OpenSans.woff2` resolves to `public/fonts/OpenSans.woff2`.

### Why not import images directly in components?

Vite-processed imports trigger bundling, hashing, and potential optimization. The `public` directory bypasses this intentionally for files that should remain unchanged or require predictable URLs, such as [`robots.txt`](https://github.com/every-app/open-seo/blob/main/robots.txt), [`manifest.json`](https://github.com/every-app/open-seo/blob/main/manifest.json), or fonts referenced in global CSS.

### Do public assets require special Cloudflare Workers routing?

No. The `wrangler.jsonc` configuration in Open SEO automatically handles static asset serving. A request to `/any-file.png` checks the bundled `public` assets before falling through to application routes—no explicit worker code needed.

### What happens if a public asset is missing?

The Cloudflare Worker returns a 404 response for unmatched paths. Since `public/` assets are served before application logic executes, missing files do not reach your React router or API handlers.