Migrate from Vercel to Azion
Move a Vercel project to Azion: deploy it, rebuild functions, rules, and cache, move Blob and Edge Config data, then switch DNS.
A Vercel project keeps its behavior in several places: build settings, vercel.json, server functions, Blob stores, Edge Config, firewall rules, and a production domain. Migrating it means rebuilding each piece on Azion, then proving the project responds correctly before the domain moves.
On Azion, an application and its rules handle delivery, routing, and cache. Functions runs the API routes and server code, and AI Inference runs models. KV Store takes the Edge Config data, and Object Storage takes the Blob files. Firewall filters requests, a workload answers on the domain, and Edge DNS can host the zone. Real-Time Metrics, Real-Time Events, and Data Stream report on traffic.
The stages below follow the order a migration runs: inventory, deployment, code and rules, data, security, monitoring, and DNS. Most code changes are narrow ones: how a function reads a variable, opens storage, and calls a model. If near-zero downtime is not required, move in phases inside maintenance windows. Each window stops writes, so no data has to stay in sync across two platforms.
Select an interface, and the prerequisites and procedures below switch to it:
Prerequisites
- An Azion account. To open one, sign up in Azion Console, as Create an account describes.
- Access to the Vercel project: its repository, its settings, and its environment variables.
- Access to the DNS records, or to the registrar, of each domain that moves.
curlanddig, to test responses and DNS answers.
- Access to Azion Console. For the sign-in steps, refer to Access Azion Console.
Inventory the Vercel project
Start with a single project that exercises the whole path and still moves fast. A good first candidate has one production-like domain, a few routes, redirects, and headers, and one function. It also has one Blob store, one Edge Config store, and one analytics workflow. Record each step as you go, then repeat the same order for the remaining projects. Prove that the first deployment builds and runs on Azion before you move its domains, storage, or firewall.
List what the project depends on before you create anything on Azion:
- Projects, teams, production deployments, and preview deployments.
- Build commands, output directories, framework presets, and install commands.
vercel.json, the framework configuration, middleware, and route definitions.- Environment variables for production, preview, and development.
- API routes, server actions, functions, and Fluid Compute workloads.
- Redirects, rewrites, headers, cache behavior, and image optimization settings.
- AI routes, model providers, and AI Gateway settings.
- Blob stores, Edge Config stores, feature flags, and experiments.
- Firewall rules, WAF controls, rate limits, bot protections, and deployment access controls.
- SAML Single Sign-On and the team roles that depend on it.
- Observability dashboards, Speed Insights, Web Analytics, alerts, log workflows, and Marketplace integrations.
- Domains, DNS records, nameservers, and the status of each certificate.
Every entry has a stage of its own on this page. Map each Vercel product to Azion names the destination of each one.
Map each Vercel product to Azion
Find each item of the inventory in the first column. The last column names the Azion destination, and the stage named after that destination moves it. Every Vercel product on the list has a destination, so no row carries a dash.
| Vercel product | What it covers | Destination on Azion |
|---|---|---|
| Advanced Deployment Protection | Access control for deployment URLs through authentication, trusted IPs, passwords, or bypasses | Firewall, Rules Engine for Firewall, and Network Lists |
| AI Cloud | Building and running AI applications | AI Inference, Functions, and Applications |
| AI Gateway | One endpoint for model access, routing, fallbacks, retries, usage monitoring, and observability | AI Inference, Functions, and Real-Time Events |
| AI SDK | A TypeScript toolkit for AI applications, agents, streaming interfaces, and tool calls | AI Inference, called with Azion.AI.run() from a function |
| Bot Management | Detection, mitigation, challenge, allow, and block controls for automated traffic | Bot Manager and Bot Manager Lite |
| BotID | Bot verification for sensitive actions, invisible to the user | Bot Manager |
| CI/CD and Preview Deployments | Builds from Git, with a preview environment for each change | Applications and the Azion CLI |
| Content Delivery Network | Caching, routing, compression, TLS, redirects, and rewrites | Applications, Cache, and Rules Engine for Applications |
| Domains and DNS | Custom domains, DNS records, nameservers, and certificate automation | Workloads, Edge DNS, and Certificate Manager |
| Edge Config | A replicated store for feature flags, experiments, redirects, and configuration | KV Store |
| Environment Variables | Values for the production, preview, and development environments | Environment variables, stored on the account |
| Fluid Compute | A server-side compute model for concurrent dynamic workloads | Functions |
| Headers | Custom request or response headers for routes | Rules Engine for Applications |
| Image Optimization | Image transformation and delivery on request | Image Processor |
| Microfrontends | Frontend projects deployed on their own, behind one routing layer | Applications and Rules Engine for Applications |
| Observability | Monitoring of traffic, builds, functions, external API calls, performance, errors, and usage | Real-Time Metrics, Real-Time Events, and Data Stream |
| Observability Plus | Longer retention, metrics, request data, queries, notebooks, monitoring, and alerts | Real-Time Metrics, Real-Time Events, and Data Stream |
| Platform Security | DDoS mitigation, TLS, platform firewalling, access controls, and security monitoring | Firewall, DDoS Protection, and Network Shield |
| Production Deployments | Builds promoted to the domains customers use | Applications and the Azion CLI |
| Projects | The build, deployment, environment, domain, and runtime settings of one project | Applications, served by a workload |
| Redirects and rewrites | Path and host routing from project settings, framework configuration, or vercel.json | Rules Engine for Applications |
| SAML Single Sign-On | Workforce sign-in through a SAML identity provider | Single Sign-On |
| Speed Insights | Real-user performance monitoring based on Core Web Vitals | Edge Pulse and Real-Time Metrics |
| Vercel Blob | Object storage for files, uploads, images, documents, and videos | Object Storage |
| Vercel CLI | Projects, deployments, logs, domains, and environment variables from a terminal | Azion CLI |
| Vercel Firewall | Traffic rules, IP blocks, rate limits, redirects, challenges, Attack Mode, and exceptions | Firewall, Rules Engine for Firewall, and Network Lists |
| Vercel Functions | Server-side functions for APIs, dynamic pages, and backend integrations | Functions |
| Vercel Marketplace | Integrations for databases, storage, authentication, AI, observability, CMS, commerce, messaging, and security | Marketplace |
| Web Analytics | Page views, visitors, referrers, demographics, custom events, and feature usage | Edge Pulse and Real-Time Metrics |
| Web Analytics Plus | Longer reporting windows and more attribution data | Edge Pulse and Real-Time Metrics |
| Web Application Firewall | Managed and custom protection against application-layer attacks | Web Application Firewall |
To check a function before it serves traffic, Preview deployment runs it against a simulated request inside the Azion Console code editor. It tests one function, and it does not create a separate environment for each change.
Deploy the project on Azion
A Vercel project turns into an application on Azion, and a workload serves that application on a domain. Vercel reads the build configuration from vercel.json, the framework configuration, and the project settings. Azion reads it from azion.config.js, which some presets name azion.config.mjs or azion.config.cjs.
| Task | Azion CLI |
|---|---|
| Install | curl -fsSL https://cli.azion.app/install.sh | bash, or brew install azion |
| Sign in | azion login |
| Run locally | azion dev |
| Deploy | azion link, then azion deploy |
Azion supports 19 frameworks and 5 generic presets. Neither interface detects the framework on its own: Azion Console offers six presets to pick from, and the Azion CLI lists the presets in a picker.
The API builds the chain one resource at a time: the application, its rules, and the workload. To create them in order, refer to Applications quickstart.
Azion gives the workload a domain under map.azionedge.net. Test the project there before any production domain moves. Request the root path, with the workload domain in place of <your-workload-domain>:
The answer carries the status code, headers, and body that the project serves for /. Repeat the request for each route that matters, such as the health path of an API:
Until the binding to the application propagates, a new workload answers with a placeholder 404. This can take several minutes, and no duration is guaranteed. Send the request again until the answer comes from the project.
When the build fails on Azion, compare the preset with the framework of the Vercel project. Then check build.preset, build.entry, and build.bundler in azion.config.js, along with the install command and the package scripts. The build block carries no field for a build command.
Move environment variables
A project reads API keys, database credentials, authentication secrets, third-party endpoints, AI provider tokens, and feature flags from its variables. When one of them is missing on Azion, the deployment still succeeds, and the project fails at run time.
Gather every variable before you change any code. On Vercel, they come from these places:
- The environment settings of the project.
- Variables managed with the Vercel CLI.
- The framework
.envfiles used in local development. - AI provider keys and model gateway settings.
- Blob tokens and storage credentials.
- Edge Config IDs, tokens, and keys.
- The CI/CD environment.
- Configuration written into the source code.
Azion stores variables on the account, up to 100 of them. Each one has a key, a value, and a flag that marks it as a secret. A function reads a variable with Azion.env.get().
To create the variables in Azion Console, go to the Account menu and open the Variables page. Add each variable with its key and its value, and mark a credential as a secret.
Then update the code that reads each variable:
A deployed function also accepts process.env.API_KEY. Under azion dev, a function reads the .env file of the project instead of the account. Without a .env file, it reads the whole shell environment. When a function reports a variable as not found, check that the account holds it and that the code reads it with Azion.env.get(). Feature flags and settings kept in Edge Config go to KV Store instead, in Move Edge Config data to KV Store.
Move API routes and server functions
API routes, server actions, webhooks, authentication, personalization, AI orchestration, and backend calls usually live in Vercel Functions and Fluid Compute. On Azion, this code runs in Functions. A function holds the code, a function instance runs it on one application, and a rule chooses the requests that reach it.
| Aspect | Vercel Functions | Azion Functions |
|---|---|---|
| Handler | handler(req, res) | fetch(request, env, ctx), with a standard Request and Response |
| Variables | process.env.VARIABLE | Azion.env.get('VARIABLE'), or process.env.VARIABLE |
| Routing | The file path of the route | A rule with the Run Function behavior |
| Blob and Edge Config | The Vercel SDKs | Object Storage and KV Store runtime APIs |
On a deployed function, env is an empty object, and ctx carries args and waitUntil. The handler reads the body from the Request and returns a Response:
Vercel maps a request to a function by its file path, such as app/api/users/[id]/route.ts or pages/api/users/[id].ts. On Azion, a rule of the application maps it instead. The rule below sends each GET request for /api/users/<id> to a function instance:
Run Function (run_function) takes the ID of the instance, not the ID of the function. The application needs Application Accelerator and Functions turned on, and a new application already has Functions on. For the behavior, refer to Run Function.
To create the function, its instance, and the rule in Azion Console, follow the Console panels of Functions quickstart. Before you add the Run Function rule, turn on Application Accelerator under Modules, in the Main Settings tab of the application.
A request that matches the rule now runs the function. When an API route fails on Azion, confirm that the handler is fetch(request, env, ctx). It must parse the request with standard Web APIs, not with helpers of the Vercel runtime.
Recreate redirects and rewrites
Redirects keep search rankings, campaign links, backlinks, and bookmarks working, and a broken one loses traffic. Vercel defines routing in framework routes, middleware, vercel.json, the project settings, and CDN behavior. Azion keeps redirects and rewrites in the rules of the application, which Azion Console, the API, the CLI, and azion.config.js all write. Each rule joins criteria to behaviors and belongs to the Request Phase or the Response Phase.
This vercel.json entry moves every path under /old-blog/ to the same path under /blog/, with a permanent redirect:
| Aspect | Vercel | Azion |
|---|---|---|
| Configuration | vercel.json, framework configuration, and project settings | Rules Engine for Applications |
| Pattern matching | Path patterns such as /old-blog/:path* | Regular expressions with the matches operator, such as ^/old-blog/(.*)$, plus starts_with and is_equal |
| Captured values | :path* | %{name[index]}, such as %{capture[1]}, from a Capture Match Groups behavior in the same rule |
A criterion only selects the requests, and it captures nothing. The capture is the job of capture_match_groups, and the redirect is redirect_to_301. To carry part of the old path into the target, put a Capture Match Groups behavior before the redirect, in the same rule. That behavior requires Application Accelerator on the application. The captured array is local to its rule, so no other rule can read it. For every argument, refer to Capture Match Groups and Redirect To.
The Azion rule matches the old path, captures the rest of it in capture, and redirects with 301 to the new path:
To create the redirect with the Azion CLI, save the rule above as rule.json. Then create it in the request phase of the application:
Request an old path to test the redirect:
The answer is 301 Moved Permanently, with a location header that ends in /blog/post. A nested path such as /old-blog/a/b redirects to /blog/a/b. A new rule can take a few minutes to propagate, so wait and retry an unexpected answer before you diagnose it.
To serve another path without redirecting, use Rewrite Request with the same captures. Move each remaining route the same way:
- Translate each Vercel route pattern into Rules Engine criteria and regular expressions.
- Move simple redirects into rules.
- Use Functions for dynamic rewrites, authentication, signed URLs, and external lookups.
- Test trailing slashes, locale prefixes, and canonical paths.
- Before the cutover, compare the cache headers and the redirects that search ranking relies on.
For moves that affect search, prefer permanent redirects and avoid redirect chains. When a redirect or a rewrite behaves unlike the Vercel route, test its capture groups and the criteria that its pattern turned into.
Recreate custom headers
Headers drive caching, security, and browser behavior. On Vercel, routes add request or response headers. On Azion, Add Request Header changes the request sent to the origin. Add Response Header, in a Response Phase rule, changes the answer sent to the user.
| Aspect | Vercel | Azion |
|---|---|---|
| Configuration | Route headers in the project | Rules Engine for Applications, in Azion Console, the API, or azion.config.js |
| Phases | Request or response | Request Phase and Response Phase |
This azion.config.js adds two security headers to each response of the application:
Each value has the form Name: value. Azion Console refuses any other shape with Header must follow the header-name: value format. A value can also carry a rule variable, such as X-Docs-Uri: ${uri}, which expands at run time. The headers reach a domain only through a workload that serves the application. Run azion deploy, then read a response:
The answer includes x-frame-options: SAMEORIGIN and x-content-type-options: nosniff. A 404 that Azion generates with no origin carries neither header.
Recreate cache settings
A Vercel project can mix CDN caching, framework cache settings, dynamic rendering, static generation, and per-route behavior. On Azion, a cache setting holds both the TTL and the cache key. A rule with Set Cache Policy then applies the setting to the requests it matches. Every cache setting belongs to one application, so each call that creates or changes one names that application.
| Aspect | Vercel | Azion |
|---|---|---|
| Cache configuration | Framework cache behavior, CDN settings, and headers | Cache settings, which rules apply |
| Cache key | Platform and framework behavior | The Cache vary by controls of the cache setting, from Cache variation, which require Application Accelerator |
| Purge | Redeploys and cache invalidation | Real-Time Purge, by URL, cache key, or wildcard |
| Stale content | Framework and CDN controls | Stale cache, which serves an expired copy when revalidation fails |
| Fewer requests to the origin | The managed CDN | Tiered Cache and Origin Shield |
The TTL is the Max Age of each cache setting, from 0 to 31,536,000 seconds, and it defaults to 60. Without Application Accelerator on the application, a value under 60 is refused with 21021. With Tiered Cache on, a cache setting needs Override cache behavior and at least 3 seconds. Stale cache honors the stale-while-revalidate that the origin sends, or keeps a 300-second window under Override cache behavior. It starts on in Azion Console and off in the API and the CLI.
Create the cache setting
The dynamic-cache setting in this section keeps a copy in the browser for 300 seconds and in the Azion cache for 3,600 seconds. Tiered Cache is on.
The CLI flags cannot set Max Age, the cache behavior, or Tiered Cache, so the command reads the body from a file. To create dynamic-cache with the Azion CLI, first save this body as cache-setting.json:
Tiered Cache requires "behavior": "override" in modules.cache. Create the setting on the application:
The rule in Apply the cache setting to a path takes this ID as <cache-setting-id>.
Apply the cache setting to a path
A cache setting has no effect until a rule names it with Set Cache Policy. The rule in this section applies dynamic-cache to every path under /products/.
To create apply-dynamic-cache with the Azion CLI, save this body as rule.json, replacing <cache-setting-id> with the setting ID:
Create the rule in the request phase:
Azion refuses to delete a cache setting while a rule applies it, with 400 and code 21014. Change or delete the rule first.
To vary the cache by query string, cookie, or device, use the Cache vary by controls of the cache setting. Cache vary by Devices with the Allowlist behavior keeps one copy for each device group you select from the Device Groups tab of the application. All three controls require Application Accelerator.
Purge cached content
A redeploy invalidates the Vercel cache. On Azion, Real-Time Purge removes objects before their TTL ends, by a list of URLs, a list of cache keys, or one wildcard expression. A URL purge takes up to 50 items, and a wildcard purge takes one expression per request. A purge item whose domain is outside your account is refused with 400 and code 30003.
To purge from Azion Console, follow Purge cached content.
Purge is a top-level endpoint, not one nested under applications. Only a cache key purge, at /v4/workspace/purge/cachekey, reaches Tiered Cache with "layer": "tiered_cache". A URL or wildcard purge with that layer fails with 30001. When cached content behaves unlike Vercel after the move, compare the TTLs, the cache key, and the rules with the Vercel project.
Serve optimized images
Vercel transforms an image through a URL that the framework generates. On Azion, Image Processor transforms an image when the request carries the ims query parameter. It resizes, crops, fits, fills, rotates, watermarks, sets the quality, and converts the format. It stores nothing: the source image comes from the origin of the application, an HTTP server or an Object Storage bucket.
The first line below is one framework-generated URL, whose pattern varies by framework. The second asks Image Processor for the same image at 1,200 pixels wide. The third also carries the quality, as q=75 does:
| Syntax | Result | Example |
|---|---|---|
?ims=WxH | Resizes to the width and the height, cropping to fit when both are set | ?ims=400x300 |
?ims=Wx | Resizes to the width, with the height in proportion | ?ims=400x |
?ims=xH | Resizes to the height, with the width in proportion | ?ims=x300 |
?ims=fit-in/WxH | Fits the image inside the dimensions, never enlarging it | ?ims=fit-in/400x300 |
?ims=fit-in/WxH/filters:fill(Color) | Fits the image and fills the rest of the canvas with a color | ?ims=fit-in/400x300/filters:fill(white) |
Image Processor serves WebP when the Accept header of the browser allows it. AVIF needs ?ims=filters:format(avif) and a client that accepts image/avif. For every parameter, refer to Image Processor URL parameters.
To turn on the Image Processor module with the Azion CLI:
The application has Image Processor on, and its rules can carry the optimize_images behavior.
Image Processor acts only on a request that a rule with Optimize Images matches, and delivers any other request unprocessed. A Request Phase rule with ${uri} matches \.(jpg|jpeg|gif|bmp|png|ico|webp|avif) covers the usual image files. To cache one copy for each ims value, turn on Application Accelerator and vary the cache by query string. For the rule and the cache key, refer to Image Processor quickstart. For an application that already serves traffic, refer to Configure Image Processor on an application.
Before the cutover, request a few typical image URLs. When an image comes back untransformed, confirm that Image Processor is on, that the rule matches, and that the URL carries ims.
Move AI routes to AI Inference
An AI route on Vercel mixes application code, model calls, streaming, tool calls, provider routing, usage tracking, and observability. On Azion, a function makes the model call, either to AI Inference or to an external provider. Provider credentials move into environment variables.
| Concern | On Vercel | On Azion |
|---|---|---|
| Model access | AI Gateway and provider integrations | AI Inference models, or an external provider called from a function |
| SDK | AI SDK | Azion.AI.run() and standard JavaScript APIs |
| Streaming | Framework and SDK streaming responses | Functions, with the Web APIs for streams |
| Observability | AI Gateway and Observability | Real-Time Events, Real-Time Metrics, and Data Stream |
| Routing and fallbacks | AI Gateway | Provider routing logic inside the function |
AI Inference runs a catalog of open-source models: large language models, vision language models, an embedding model, and a reranker. A model is not an object you create, and Azion hosts no inference endpoint for it. A function calls a model by its ID with Azion.AI.run(), with no credential:
Under azion dev, Azion.AI is undefined, so test the call on a deployed function. For the request fields, refer to Model invocation and the AI runtime API.
A route that keeps an external provider forwards the request body and returns the answer of the provider. This function reads the provider key from a variable:
For an OpenAI-compatible /v1/chat/completions endpoint on AI Inference, deploy the AI Inference Starter Kit template. It creates an application and a function that serve that endpoint. Access Azion Console > Create, select the template, and select Deploy. The Azion CLI has no template flag.
To move each AI route:
- Record each model, provider, route, and fallback the project uses.
- Move the provider credentials into environment variables.
- For each route, decide whether inference runs on AI Inference or at an external provider.
- Rewrite each route as a function, then route a path to it with a rule, as in Move API routes and server functions.
- Recreate the AI observability in Real-Time Events, Real-Time Metrics, and Data Stream.
- Test streaming answers, timeouts, and error handling.
Move Edge Config data to KV Store
Edge Config usually holds feature flags, experiments, redirects, configuration, and other data that a project reads often. KV Store keeps key-value pairs in namespaces, and it covers those uses plus session state and other light state. A function reaches it through Azion.KV, a runtime global that needs no import line.
Code that read Edge Config opens a namespace instead:
Azion.KV.open() is the only entry point, and it is asynchronous. The namespace must exist first, or open() throws NotFound. get() returns null for a missing key, and for an expired one too.
KV Store has no Azion Console screen and no Azion CLI command, so the namespace is created through the API in every interface. The name takes 3 to 63 characters and is case-sensitive. No interface renames or deletes a namespace, so settle on the name first.
Azion Console has no KV Store screen. Create the namespace with the API request of the API panel.
KV Store has no bulk import, and no API call, CLI command, or Console screen reads or writes keys. A deployed function writes them with kv.put(). To move the data:
- Export the keys, values, metadata, and per-environment values from Edge Config.
- Keep the key prefixes and naming conventions where you can.
- Write each key from a deployed function with
kv.put(). - Write down what the code does when a key is missing.
- Exercise every read path before production traffic reaches Azion.
- Confirm that values keep their encoding and their JSON serialization.
- Rebuild any flag or experiment workflow that depended on Vercel tooling.
The function writes the same key at most once per second. A value takes up to 25 MB, a key up to 512 bytes, and the metadata up to 1,024 bytes. An expiry goes in the expiration option, in Unix seconds, or in expirationTtl, in seconds with a minimum of 60. A write becomes visible everywhere within 60 seconds, or within the cacheTtl of the read. When data is missing after the move, rewrite the keys, check the namespace name, and test the default values.
Move Blob files to Object Storage
Vercel Blob holds files such as images, documents, videos, and uploads. Object Storage keeps them as objects in buckets, and it speaks the S3 protocol. S3 tools, the API, the Azion CLI, and the runtime API of a function all reach it. Object management goes through the S3 endpoint s3.us-east-005.azionstorage.net, in the region us-east-005.
A function that wrote with the Blob SDK writes with the Storage class of the Object Storage runtime API. The constructor takes the bucket name and no token. put takes an ArrayBuffer or a ReadableStream, not a string:
Under azion dev, the module stores objects on the local disk and behaves differently, so test storage calls on a deployed function.
A migration script that runs in Node.js reaches the S3 endpoint with any S3 SDK, given the endpoint, the region, and a key pair:
The key pair comes from an Object Storage credential, created in Azion Console or with a POST request to https://api.azion.com/v4/workspace/storage/credentials. The secret_key appears only in the create response. A migration needs listBuckets, listFiles, and writeFiles on the credential, plus listAllBucketNames to list the buckets. S3-compatible tools such as s3cmd, rclone, and the AWS CLI handle the bulk copy. For the S3 operations Object Storage accepts, and the s3cmd form of each, refer to S3 compatibility.
Create the destination bucket before a bulk copy, in Azion Console, the API, or the CLI. s3cmd mb and s3cmd rb are refused with 403 AccessDenied. A bucket name takes 6 to 63 characters, is unique across all accounts, and cannot start with azion.
To create the bucket and upload files in Azion Console, refer to Create and modify a bucket and Upload and download objects. Azion Console refuses a single upload over 300 MB. The API and S3 tools have no such limit.
Bucket access for workloads is read_only, read_write, or restricted, and a credential works regardless of it. A file that Vercel kept private behind a token or a URL pattern relies on the access level of its bucket and on application logic. Users receive objects through an application and a connector, not through the S3 endpoint. That path puts cache, firewall rules, and the production domain in front of the files. To connect the bucket, refer to Use a bucket as an application origin. When files are missing after the move, export them again, then check the object keys and the access level of the bucket.
Protect the application with WAF
Web Application Firewall (WAF) scores requests against eight threat families: cross-site scripting, directory traversal, evading tricks, file upload, identified attack, remote file inclusion, SQL injection, and unwanted access. A WAF rule set holds a sensitivity for each family, and a firewall rule applies the rule set with Set WAF. The workload deploys the firewall next to the application, and in Azion Console the Deployment Settings of the workload select it.
| Aspect | Vercel | Azion |
|---|---|---|
| Managed rules | WAF protections | One managed rule set, scored per threat family |
| Custom traffic rules | Firewall rules | Rules Engine for Firewall |
| IP controls and trusted IPs | IP blocks and allowlists | Network Lists, matched with ${network} |
| Actions | Block, challenge, redirect, and allow | Deny, Drop, Set Rate Limit, Set WAF, Run Function, and Set Custom Response |
| Password or authentication gates | Advanced Deployment Protection | Firewall rules, and Functions for custom logic |
mode is required on every Set WAF behavior, and it has no default. Start in Logging to see what the rule set would block, then switch to Blocking. In Blocking, a request that the rule set blocks receives 400. Compare the false positives, the rules that trigger most, and the exceptions the application needs before you block. To keep a legitimate request from matching, add a WAF exception or use the Tuning tab.
To bind a firewall with the Azion CLI, pass --firewall-id to the workload deployment. For the rule set and the rule, follow the WAF quickstart.
A Vercel deployment protection rule becomes a firewall rule that denies requests from outside an approved network. Firewall variables differ from application variables: the path is ${request_uri}, and an address range goes in a Network List matched with ${network}. This rule denies /admin to any client outside a Network List that holds 10.0.0.0/8:
A second rule with ${request_uri} starts with /preview/ protects preview paths the same way. The ${network} criterion requires Network Shield on the firewall. To write the rules, refer to Create a firewall rule. When a rule blocks valid users, return the rule set to Logging, compare the events, and tune the criteria.
Rely on DDoS Protection
DDoS Protection covers every workload, with nothing to create or configure. It takes over the DDoS mitigation of Vercel Platform Security. It mitigates volumetric, protocol, and application-layer attacks on layers 3, 4, 6, and 7: UDP and ICMP floods, SYN floods, packet fragmentation, HTTP floods, and slowloris, among others.
| Aspect | Azion DDoS Protection |
|---|---|
| Activation | Automatic, and it cannot be turned off |
| Layers | 3, 4, 6, and 7 |
| Billing | Unmetered for layers 3 and 4. Layer 7 mitigation can generate chargeable traffic |
| Customization | Custom firewall rules |
Each firewall shows the DDoS Protection Unmetered switch in Main Settings > Modules, always on, and modules.ddos_protection is read-only in the API. DDoS Protection has no thresholds, no per-rule switches, and no alerts. For targeted mitigation, write custom rules on the firewall bound to the workload. The Security Response Team is an add-on to Enterprise and Mission-Critical support. For the attack types, refer to Attack mitigation.
Network Shield is a separate firewall module. It checks the client address against a Network List of IP addresses, CIDR ranges, ASNs, or countries, through the ${network} criterion. Use it to allow or block sets of clients, to restrict countries, or to rate-limit a set of clients. For the setup, refer to Network Shield quickstart.
Recreate bot management
Vercel Bot Management and BotID move to Bot Manager, which scores each request and acts on the score. Bot Manager Lite is the Marketplace function that every plan includes, and the full Bot Manager is available on Enterprise.
| Aspect | Vercel | Azion Bot Manager |
|---|---|---|
| Controls | Detection, mitigation, challenge, allow, and block | Static rules, a dynamic behavioral method in the full Bot Manager, device fingerprints, and reputation Network Lists |
| Challenge | Challenge controls | A JavaScript Tag for fingerprinting, and ALTCHA through the redirect action |
| Actions | Allow and block | allow, custom_html, deny, drop, hold_connection, random_delay, and redirect |
| Sensitive actions | BotID | Bot Manager, with function logic where a route needs more |
Bot Manager Lite scores a request with 26 static rules against a threshold that defaults to 30, and its default action is deny. It can also check the client against reputation Network Lists. Tolerance levels belong to the dynamic rules of the full Bot Manager, which Bot Manager Lite lacks.
To set up Bot Manager Lite in Azion Console:
Access Azion Console > Marketplace, search for Bot Manager Lite, then select Install. The installation applies at once.
Go to Firewalls, then select a firewall that has the Functions module on.
In the Functions Instances tab, create an instance of Bot Manager Lite, and set threshold and action in its JSON arguments. For more information, refer to Functions instances.
In the Rules Engine tab, create a rule with the Run Function behavior and the instance.
The firewall scores each request of the workload. For every argument, refer to Install Bot Manager Lite and Bot Manager Lite. For how a function runs on a firewall, refer to Functions for Firewall. The Radware Bot Manager integration adds third-party bot protection.
A firewall rule also blocks a client by its user agent:
${header_user_agent} requires the WAF module on the firewall, and it supports only matches and does not match. The firewall has no allow behavior: to exempt a client, add a does not match criterion to the deny rule, or order the rules. Any client can send any user agent, so verify a good bot by other means. To test the rule:
The answer is 403, with the Forbidden error page. A request with a browser user agent receives the normal answer.
Recreate rate limits
Azion limits request rates in two ways, and each covers a different part of the rate controls of Vercel Firewall. The native Set Rate Limit behavior of a firewall rule caps requests per second or per minute. It counts per client IP address or across all clients. For custom keys, custom windows, or a penalty period, use the Upstash Rate Limiting integration. It is a rate limit with penalty that runs as a firewall function.
| Capability | Native Set Rate Limit | Upstash Rate Limiting function |
|---|---|---|
| Count key | Client IP address or global | Any combination of request metadata, headers, and the hostname |
| Window | Per second or per minute | Any interval in seconds or minutes, with different limits for different times of day |
| Algorithm | Leaky bucket, counted in each data center | Fixed window, sliding window, or token bucket, counted globally |
| Response | 429, with no rate-limit header | 429 at the limit, and 403 during a penalty |
| Log-only action | None | None |
| Requirements | None | An Upstash account and Global Database |
Use the native rate limit
A Set Rate Limit behavior counts the requests that its rule matches. The criteria of the rule scope the limit, such as a path in ${request_uri}. Rate Limit Type is Req/s or Req/min, and Limit By is Client IP address or Global. Average Rate Limit takes at least 1. Maximum Burst Size takes at least 1 and applies to Req/s only. No behavior can follow Set Rate Limit in a rule. Criteria that join several paths with or share one count across all of them.
To create the rate limit with the Azion CLI, save the rule body of the API panel in a file. Pass the file with --file to the firewall rule command. For the commands, refer to Firewall quickstart.
A request beyond the rate and the burst receives 429, with the Too Many Requests error page. For how the rate and the burst admit requests, refer to Set Rate Limit.
Use the rate limit with penalty
The Upstash Rate Limiting function stores its counters in an Upstash Global Database. It therefore counts every request across the network, not in each data center. A client in a penalty receives 403 Forbidden. Otherwise, the function counts the request and returns 429 Too Many Requests once the count reaches the limit.
To set it up in Azion Console:
Access Azion Console > Marketplace, search for Upstash Rate Limiting, then select Install.
Go to Firewalls, then open a firewall with Functions turned on in Modules.
In the Functions Instances tab, create an instance. In Function, select the Upstash Rate Limiting function, then edit the JSON Arguments.
In the Rules Engine tab, create a rule with criteria such as Host matches yourdomain.com, and the Run Function behavior with the instance.
Run the CLI command that creates the workload deployment with the firewall:
The function counts the requests that the rule matches. These arguments set a sliding window of 2 requests per 20 seconds from midnight to noon UTC, with a 45-second penalty:
| Argument | Description |
|---|---|
upstash_redis_rest_url, upstash_redis_rest_token | The REST URL and the token of the Upstash database that stores the counters and the penalties |
rate_limit_prefix | A prefix for every key, which keeps two instances of the function apart |
rate_limit_key_metadata | The request metadata that forms the key, such as remote_addr |
rate_limit_key_header | The headers that form the key |
rate_limit_key_hostname | When true, the hostname is part of the key |
rate_limit_repenalize | When true, every request during a penalty restarts it |
rate_limits | The windows, at least one. When two windows overlap, the first one in the list applies |
algorithm | fixed_window, sliding_window, or token_bucket |
requests | The requests allowed in the interval |
interval | The window, as a number and s or m. For example: "120 s" |
start, end | The time of day the window covers, in 24-hour UTC. They default to 00:00 and 23:59 |
penalty_in_seconds | How long a client that exceeds the limit receives 403. Without it, the window is a plain rate limit |
max_tokens, refil_rate | The bucket size and the refill per interval of a token_bucket window. refil_rate is the spelling the function reads |
The key joins the prefix and every value the arguments select. Here, it is my_rate_limit + client IP + x-a-custom-header value + hostname, such as my_rate_limit_127.0.0.1_Value_azion.com. For the full setup, refer to Install the Upstash Rate Limiting integration.
Move account access
A Vercel team that signs in through SAML Single Sign-On keeps that pattern on Azion through Single Sign-On with an external identity provider. Azion documents SAML apps for Microsoft Entra, Google, and Okta as identity providers. SSO for team members needs an Enterprise or Mission-Critical support service, and only an Account Owner configures it.
Before the move, record the identity provider settings, the group memberships, the user access, and the runbooks that depend on them. Then move the access:
- Record each identity provider and its SAML metadata.
- Translate each Vercel team role into Azion account roles and team permissions.
- Set up SSO on Azion before most of the team moves.
- Make sure a break-glass administrator keeps a way to sign in.
- Review multi-factor authentication, the user session timeout, and the account lockout policy.
Rebuild monitoring
Vercel Observability moves to three Azion products, so production visibility, troubleshooting, and compliance reporting continue after the switch. Real-Time Metrics charts aggregates over time. Real-Time Events answers queries about single requests, and Data Stream sends logs to outside destinations. Speed Insights and Web Analytics move to Edge Pulse, in Replace Speed Insights and Web Analytics.
To plan the move, list the dashboards, reports, alerts, and log workflows the project uses. Decide which of them move to Real-Time Metrics, to Real-Time Events, to Data Stream, or to an external BI tool. Vercel Marketplace integrations move to the Azion Marketplace or to a Data Stream destination. Set up each one before production traffic moves, so no analytics gap opens at the switch.
Real-Time Metrics
| Aspect | Azion Real-Time Metrics |
|---|---|
| Data freshness | Up to 10 minutes to aggregate |
| Retention | 2 years, except 90 days for httpBreakdownMetrics and 60 days for botManagerBreakdownMetrics |
| Query method | Dashboards, Copy query, Export CSV, and the GraphQL API |
| Metrics | Requests, data transferred, status codes, cache offload, and average request time |
| Granularity | 1 minute under 2.5 days, 1 hour up to 60 days, and 1 day beyond |
The Applications dashboards chart:
- Requests: total requests, requests by method and by scheme, and Average Request Time. That chart is the average time, in seconds, Azion takes to process and answer a request.
- Status Codes: the 2XX, 3XX, 4XX, and 5XX responses, and the Requests by Status and Upstream Status table. That table separates errors from Azion and errors from the origin.
- Data Transferred: saved and missed data and bandwidth, and Edge Offload.
- Cache: Requests Offloaded, Saved Requests, and Missed Requests.
Real-Time Metrics reports no latency, time to first byte, or origin response time. To read the cache status of the requests, filter a dashboard by Upstream Cache Status, whose values include HIT, MISS, STALE, and EXPIRED. To find origin errors, filter by Upstream Status, which is 0 when the origin did not answer.
To open the dashboards, access Azion Console > Real-Time Metrics. It opens on Build > Applications > Data Transferred, over the Last 5 minutes. To narrow a dashboard to one workload, add the Domain or Workload filter. To export a chart, open its More options menu, then select Export CSV.
To query the same data, send a GraphQL query to https://api.azion.com/v4/metrics/graphql:
Replace the dates with a range inside the retention period, because a range past it returns an empty array. limit takes up to 10,000 rows and defaults to 10. The httpMetrics dataset of older queries still works, but it is deprecated. For every field, refer to Real-Time Metrics GraphQL fields and Build dashboards. For Grafana, refer to Grafana plugin custom dashboards and pre-built dashboards. To read the dashboards, refer to Analyze metrics.
Real-Time Events
| Aspect | Azion Real-Time Events |
|---|---|
| Access | Queries in Azion Console or the GraphQL API |
| Delay | Up to 30 seconds |
| Retention | 7 days. For longer retention, use Data Stream |
| Format | GraphQL responses with the fields you select |
Real-Time Events keeps the record of each request for investigation, and it needs no setup. Its data sources are HTTP Requests, Functions, Functions Console, Image Processor, Tiered Cache, Edge DNS, Data Stream, and Activity History. WAF results are fields of HTTP Requests.
To query the events in Azion Console:
Access Azion Console > Products menu > Observe > Real-Time Events.
Select the data source, such as HTTP Requests.
Set the Time Filter, which opens on the last 15 minutes, and add conditions in Filter by.
The results table lists the events. Select a row to open the whole record.
To query the same data, send a GraphQL query to https://api.azion.com/v4/events/graphql. The workloadEvents dataset holds the HTTP requests:
Replace the dates with a range inside the last 7 days. upstreamResponseTime and upstreamHeaderTime exist only as raw fields of workloadEvents, and upstreamResponseTime reads - for a response served from cache. For every field, refer to Real-Time Events GraphQL fields and Investigate requests with the GraphQL API.
Data Stream
| Aspect | Azion Data Stream |
|---|---|
| Access | Push to an external destination |
| Delay | Batches of 2,000 records or 60 seconds, delivered within 3 minutes |
| Retention | Set by the destination |
| Format | Templates that select the fields |
| Destinations | 11 types, listed below |
A stream reads one data source, such as Applications or WAF Events, and formats each record with a template. It delivers the records to one destination, with its credentials:
- Storage: Amazon S3, Azure Blob Storage, and Azion Object Storage, through the S3 type.
- Monitoring: Datadog, Splunk, Elasticsearch, and Azure Monitor.
- Streaming: AWS Kinesis Data Firehose and Apache Kafka.
- Analytics: Google BigQuery.
- Security: IBM QRadar.
- Custom: Standard HTTP/HTTPS POST.
A stream needs exactly one of sampling or a workload filter. Saving an active sampled stream deactivates every other stream on the account. Filtered streams coexist.
To create a stream with the Azion CLI, follow the CLI panel of the Data Stream quickstart.
For an Object Storage destination, the credential needs listAllBucketNames, listBuckets, listFiles, and writeFiles, or every send fails with 503. For the fields, refer to Stream settings and Endpoints. For destination guides, refer to Amazon S3, Azion Object Storage, Datadog, Splunk, Elasticsearch, Kinesis, BigQuery, and Configure sampling.
Replace Speed Insights and Web Analytics
Edge Pulse takes over the real-user measurements of Speed Insights and Web Analytics. A JavaScript tag collects navigation, availability, latency, and bandwidth measurements from the browsers of real visitors. Real-Time Metrics covers the traffic side of the same reports.
To move the real-user monitoring:
- List the Core Web Vitals, the page-level reports, and the analytics dashboards the team reads.
- Decide which of them move to Edge Pulse, to Real-Time Metrics, or to an external BI tool.
- Rebuild custom event tracking where the project needs it.
- Review the privacy and consent requirements of the tag.
- After the switch, compare the user experience measurements with the Vercel baseline.
Prepare the certificate
Certificate Manager holds the certificates that workloads serve. Prepare the certificate before the domain points to Azion, so users reach the project over HTTPS from the first request.
| Area | Azion Certificate Manager |
|---|---|
| Certificate options | Azion SAN, Let’s Encrypt, custom certificates, and Trusted CA certificates for mTLS |
| Default managed certificate | Let’s Encrypt for your own domains. Azion SAN covers the azionedge.net workload domain and the azion.app hostname |
| Custom certificates | Upload of a certificate and its private key, single-domain or SAN, with RSA 2048 or P-256 keys |
| Validation | Let’s Encrypt HTTP-01 or DNS-01 challenges |
| Renewal | Let’s Encrypt certificates renew from 30 days before their 90-day expiry. Custom certificates follow your own lifecycle |
| Origin encryption | Transport Protocol Policy of the connector: Preserve, Force HTTPS, or Force HTTP |
| mTLS | Trusted CA certificates. Azion SAN does not support mTLS |
Once you choose a Let’s Encrypt preset, Azion issues the certificate at no additional cost. The challenge depends on where DNS answers:
- HTTP-01 needs the hostname, and every alternative name, to already point to Azion.
- DNS-01 works before the move. At an external DNS provider, add a CNAME from
_acme-challenge.<domain>to<domain>.letsencrypt.azion.com. In Edge DNS, the record is automatic.
For a move from Vercel, use DNS-01, so the certificate is active before the domain switches.
To set mTLS or the certificate of a workload with the Azion CLI, run azion update workload --file with the workload body. For the certificate commands, refer to Certificate Manager quickstart.
When the certificate does not become active, read its status and status_detail. For HTTP-01, check that the hostname points to Azion. For DNS-01, check the _acme-challenge CNAME. Retries continue on schedule, so a fixed record issues the certificate later. For the issuance rules, refer to Issuance and renewal.
mTLS needs a Trusted CA certificate, and an Azion-generated certificate cannot be the Trusted CA. Sales activates mTLS on the account. Then set mtls.enabled, mtls.config.certificate, and verification, enforce or permissive, on the workload through the API or azion update workload --file. mTLS works over HTTPS only. For the steps, refer to Configure mTLS on a workload and mTLS.
Move DNS zones to Edge DNS
Moving the zone to Edge DNS gives Azion every record of the domain, including the apex. Every zone uses the same three nameservers: ns1.aziondns.net, ns2.aziondns.com, and ns3.aziondns.org. Skip this stage to keep the current DNS provider and point only subdomains, as Point the domain to the workload shows.
| Aspect | Azion Edge DNS |
|---|---|
| Nameservers | ns1.aziondns.net, ns2.aziondns.com, and ns3.aziondns.org for every zone |
| Record types | A, AAAA, ANAME, CAA, CNAME, DS, MX, NS, PTR, SRV, and TXT |
| DNSSEC | Supported |
| API | /v4/workspace/dns/zones |
Recreate each record of the current zone with its type:
| Record | Use on Azion |
|---|---|
| A | IPv4 address |
| AAAA | IPv6 address |
| ANAME | Alias of the apex to an Azion hostname, such as the workload domain. Its TTL must be 20 |
| CNAME | Alias to another name. It holds exactly one value and cannot sit at the apex |
| MX | Mail exchange, with its priority |
| TXT | Text, such as SPF and DKIM |
| SRV | Service records, one per name |
| CAA | Certificate authorities allowed to issue for the domain |
| NS | Delegation of a subdomain |
| DS | Delegation signer of a signed child zone |
| PTR | Reverse lookup |
Edge DNS refuses other types, such as SOA. A, AAAA, ANAME, DS, MX, and NS records hold up to 10 values each.
To create zones and records with the Azion CLI, follow the CLI panel of the Edge DNS quickstart. Boolean flags need =, such as --active=false.
With DNSSEC on, Edge DNS shows four DS values: Key Tag, Algorithm 13, Digest Type 2, and Digest. Reload the page after you save to see them. Add them at the registrar, which can take up to 48 hours to publish them. For the steps, refer to DNSSEC.
To check the move, query the nameservers and the records:
The first command lists the three Azion nameservers once the registrar change propagates. The second shows the answer of Edge DNS before the change reaches every resolver. With DNSSEC on, the third returns two DNSKEY records, with flags 257 and 256 and algorithm 13. Do not query a new name before its record exists, because Edge DNS caches the negative answer for one hour. For more commands, refer to Run the dig command and Run the traceroute command.
Point the domain to the workload
The domain moves last, because the DNS change is the switch: once the domain resolves to the workload, users reach the project through Azion. It affects users, search ranking, brand trust, and availability, so treat it as a planned cutover. Before you switch, confirm that:
- The certificate is active.
- The hostname is in the Domains of the workload. For the domain settings, refer to Domains.
- The DNS records are ready.
- The critical routes and the redirects answer as expected on the workload domain. To test them under the real hostname first, refer to Test an application through the hosts file.
- Monitoring is ready to watch the traffic after the switch.
Point each name with the record its zone allows:
| Strategy | Use it for | DNS control | Record |
|---|---|---|---|
| CNAME | A subdomain that moves quickly | The current DNS provider keeps the zone | www CNAME <your-workload-domain> |
| Nameservers | The apex and every other name | Edge DNS answers for the zone | An ANAME at the apex to the workload domain |
To check a CNAME:
The answer is the workload domain, such as xxxxxxxxxx.map.azionedge.net. DNS changes take time to propagate. Once the name resolves, request it, with the domain in place of <your-domain>:
The answer comes from the application, with the status and the headers it returns for /. When HTTPS fails, read the certificate status and confirm the hostname is in the workload. For the Console settings of the custom domain, refer to Point a domain to a workload. To move the nameservers, refer to Migrate the nameservers to Azion.