# Build and run marketing websites

A marketing, content, or web team owns the company's marketing site, campaign pages, or docs portal, and wants each change live quickly for visitors everywhere. The team builds the site with a framework and keeps its pages and content in a GitHub repository. This page connects the repository so that every push builds the site and deploys it to Object Storage, serves it through Cache, and purges the site after each deploy so the change reaches every location at once. The result is measured by time to first byte for visitors in every region, the time from a content change to a live page, and the share of requests answered from cache.

This use case does not cover e-commerce storefronts, which [Build e-commerce storefronts](/en/documentation/use-cases/build-and-run-applications/build-e-commerce-storefronts/) covers, or feature layers such as forms and A/B testing.

## Prerequisites

- An Azion account. If you do not have one, [create an account](https://console.azion.com/signup).
- An Azion account connected to your GitHub account through the Azion GitHub App. To connect it, refer to [Manage the Azion GitHub App](/en/documentation/guides/application-development/automation/azion-github-app/).

---

## Required products

| The site needs                                           | Which means                                                                                     | Product         | Documented in                                                                                                                                                                                                                              |
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Pages built from the repository on every push            | A bucket the deploy creates and fills with the build output, under a new prefix for each deploy | Object Storage  | [Import a project from GitHub](/en/documentation/guides/application-development/automation/import-an-existing-project-from-github/) and [How Azion CLI works](/en/documentation/devtools/cli/how-it-works/#from-a-project-to-a-deployment) |
| Pages and assets that answer from cache                  | The cache setting and the rules the Astro preset generates for the application                  | Cache           | [Build with Astro](/en/documentation/guides/application-development/frameworks/astro/)                                                                                                                                                     |
| A change that reaches every location once it is deployed | A wildcard purge for the site's domain after each deploy                                        | Cache           | [Purge pages when the origin publishes a change](/en/documentation/guides/application-performance/cache-and-purge/purge-on-publish/)                                                                                                       |
| Images at the size and format each page asks for         | Image Processor on the application, applied by a rule on image paths                            | Image Processor | [Image Processor quickstart](/en/documentation/platform/applications/image-processor/quickstart/)                                                                                                                                          |

---

## Reference architecture

This page builds the *Git-driven static website*: every push to the repository builds the site, and an application serves the prebuilt files from Object Storage through Cache.

```mermaid
%%{init: {"layout": "dagre", "themeVariables": {"fontSize": "13px"}, "flowchart": {"nodeSpacing": 12, "rankSpacing": 12, "padding": 6, "wrappingWidth": 70, "minNodeWidth": 40, "useMaxWidth": true}}}%%
flowchart TD
  Repo["Git repository"] -->|"push"| GH["Azion GitHub App"]
  GH -->|"build, then upload under a new prefix"| Bucket["Object Storage bucket"]
  Visitor["Visitor"] -->|"HTTPS request"| App["application"]
  App -->|"page or asset"| Cache["Cache"]
  App -->|"image path, a design option"| IP["Image Processor"]
  Cache -->|"miss"| Connector["connector to the bucket"]
  IP --> Connector
  Connector -->|"reads the current prefix"| Bucket
```

The diagram carries two flows that meet only at the bucket. The publish flow, at the top, runs from the repository through the Azion GitHub App and moves only when someone pushes. The request flow runs from the visitor through the application, where Cache answers from its copy and a miss reads a prebuilt file from the bucket. Nothing in the request flow builds or renders a page, so a visitor's request never waits on a build.

### Dataflow

1. A push to the connected repository starts a deploy, and the Azion GitHub App builds the site with the framework preset of the project, Astro on this page.
2. The deploy uploads the build output to the project's Object Storage bucket under a new storage prefix, and points the connector at that prefix. The files of earlier deploys stay in the bucket under their own prefixes.
3. A visitor's request reaches the workload on the site's domain, which hands it to the application, and the application's rules apply the cache setting the Astro preset generated.
4. Cache answers from its copy when it holds one. On a miss, the application reads the prebuilt file from the bucket through the connector, and Cache stores it for its Max Age.
5. When Image Processor is on, an image request is resized or converted from the original in the bucket, and Cache keeps each variation.
6. Once the deploy completes, a wildcard purge removes every page of the domain from Cache, and the next request for each page reads the new build from the bucket.

### Platform Resources

- **Azion GitHub App**: the integration that connects the GitHub account to Azion. It deploys every push to a connected repository, so publishing a change is a push and nothing else.
- **Object Storage**: holds the build output. Each deploy writes its files under its own prefix, which is what keeps an earlier build available for a rollback.
- **application**: the Platform Resource that delivers the site. Its connector reads the bucket, and its rules apply the cache setting that the project's configuration declares.
- **Cache**: stores pages and assets, so repeated requests never reach the bucket. Its Max Age bounds how long a page stays stale after a deploy that no purge follows.
- **Image Processor**: a design option that resizes and converts images on request, so the repository keeps one original per image.

### Other designs for this use case

- *Statically generated headless CMS website*: for teams whose editors work in a headless CMS such as ButterCMS, Cosmic, or Sanity. A static site generator reads the CMS API at build time, so publishing depends on a CMS event instead of a push, while visitors still read prebuilt files.
- *Server-rendered headless CMS website*: for teams that need content changes live without a rebuild, or pages that vary per request. Functions render each page on request with content from the CMS API, so the request and failure flows include a function and the CMS API, and freshness is a caching decision instead of a build.
- *Directly uploaded static website*: for teams that build the site in their own CI or export it from a tool. No build runs on Azion: the built files are uploaded to Object Storage with the CLI or the S3-compatible API, so versioning and invalidation decisions move to the team's own tooling.
- *Origin-hosted CMS website*: for teams that keep a CMS such as WordPress on their own servers. The CMS keeps rendering every page and the request flow reaches it on every miss, so cache bypass by path and session cookie, purge on publish, and WAF rules on the login and admin paths become the design decisions.

---

## How the design works

This section explains how the site reaches Azion on every push, and why one purge follows each deploy.

### Deploy on push

The site is an Astro project at the root of the repository, imported in Azion Console so that every push deploys it. Azion runs an Astro site as a static application: the build writes the site to `./dist`, and the deploy uploads that folder to a bucket the application serves. Astro is one of the presets the import form offers; the others are Next.js, Angular, Hexo, React, and Vue. The project must sit at the root of the repository, because the import reads it from there.

### The purge after each deploy

A deploy uploads the new build under a new prefix, and pages already in cache keep the old build until their Max Age ends. One wildcard purge for the whole domain, sent once the deploy completes, makes every page read the new build. A wildcard fits because a build can change any page, and one purge per deploy stays far below the 2,000 wildcard requests Azion accepts in a 24-hour interval. Send it once the deploy of the push completes: a purge sent while the deploy still runs can refill the cache from the old build.

```mermaid
%%{init: {"layout": "dagre", "themeVariables": {"fontSize": "13px"}, "flowchart": {"nodeSpacing": 12, "rankSpacing": 12, "padding": 6, "wrappingWidth": 70, "minNodeWidth": 40, "useMaxWidth": true}}}%%
sequenceDiagram
  participant Team as Team
  participant Azion as Azion deploy
  participant Cache as Cache
  participant Visitor as Visitor
  Team->>Azion: git push
  Azion-->>Team: Deploy complete
  Team->>Cache: Wildcard purge
  Visitor->>Cache: Page request
  Cache->>Azion: Miss, read new build
```

1. The team pushes a change, and the deploy builds and uploads the new files.
2. The deploy completes, and the bucket holds the new build.
3. The team sends the wildcard purge for `www.example.com/*`.
4. The next request for each page misses the cache and reads the new build from the bucket.

---

## Measuring results

| Metric                                          | Where to read it                                                                                                                                                                                                                         | What working looks like                                                                                        |
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| Time to first byte for visitors in every region | The `ttfb` field of Edge Pulse measurements, taken in the visitors' browsers by a tag the site's pages carry. Refer to [Edge Pulse quickstart](/en/documentation/platform/edge-pulse/quickstart/)                                        | Low and comparable across regions, because every page is a prebuilt file in cache                              |
| Time from a content change to a live page       | The time of each commit on GitHub, against the purge history of **Real-Time Purge**, which lists each purge when it is complete. Refer to [Real-Time Purge](/en/documentation/platform/applications/cache/real-time-purge/#confirmation) | The purge after each push completes, and no page waits for its Max Age to expire                               |
| Share of requests answered from cache           | **Requests Offloaded** in Real-Time Metrics, filtered to the site's domain. Refer to [Measure cache offload for a domain](/en/documentation/guides/platform/observability/measure-cache-offload/)                                        | High between deploys, with a short dip after each purge. A miss reads the bucket, never a server the team runs |

---

## Best practices

- **Roll back with a revert, not a hotfix in the Console.** Every push deploys the site, so a commit that reverts the change deploys the earlier content again through the same build, and the repository keeps the history. For a project deployed with the Azion CLI, `azion rollback` serves the files of an earlier deploy from the bucket instead. For its flags, refer to [Azion CLI rollback](/en/documentation/devtools/cli/rollback/).
- **Purge after each deploy instead of shortening Max Age.** A short Max Age sends more requests to the bucket and still leaves a stale window after every push. The purge refreshes every page once, and Max Age stays a safety bound for a missed purge.
- **Keep one original per image.** Image Processor resizes and converts on request, so the repository needs no copy per size. For the rule and the cache setting that keep each variation, refer to [Image Processor quickstart](/en/documentation/platform/applications/image-processor/quickstart/).

---

## Guides in this use case

- [Purge pages when the origin publishes a change](/en/documentation/guides/application-performance/cache-and-purge/purge-on-publish.md): Sends the wildcard purge after each deploy, and confirms it completed.
- [Import a project from GitHub](/en/documentation/guides/application-development/automation/import-an-existing-project-from-github.md): Imports the example-site repository with the Astro preset, so every push deploys the site.
- [Build with Astro](/en/documentation/guides/application-development/frameworks/astro.md): Creates the example-site project with the Astro preset and builds it once, so it carries azion.config.mjs.
- [Check the cache status of a response](/en/documentation/guides/application-performance/cache-and-purge/check-page-cache-time.md): Reads the x-cache header that every check in this use case relies on.
- [Configure Image Processor on an application](/en/documentation/guides/application-performance/delivery-optimization/process-images.md): Confirms that each image variant of the site is processed and cached.
