CloudFront Not Updating? Cache Invalidation Done Right

Logeshwaran.C

If CloudFront is still serving the old file after you updated it in S3, the fix is either of two things: run a cache invalidation on the path (console, CLI, or boto3) to force CloudFront to re-fetch it right now, or wait out the cache's Time to Live (TTL) if you can't act immediately. Here's the part almost nobody mentions: CloudFront will hold onto your file for a full 24 hours by default, using the AWS-managed CachingOptimized policy, even if you never set a Cache-Control header on the S3 object at all — the caching isn't a bug you triggered, it's the default behavior working exactly as designed.

⚡ Quick Answer

Force it right now → CloudFront console → your distribution → Invalidations tab → Create invalidation → enter the path (e.g. /images/logo.jpg or /*)

From a script or CI/CD jobaws cloudfront create-invalidation --distribution-id YOUR_ID --paths "/path/to/file"

One line if you only read this box: invalidation usually finishes within a few minutes and the first 1,000 paths a month are free. For the full steps, cost limits, and the fix that stops this from happening every deploy, see creating an invalidation and fixing the root cause below.

Wait! if you are a new reader here and don't know about AWS because you didn't had money to learn or from Non Tech background, don't feel bad! I had create one hub, start from here: Learn AWS its free, and all in plain english so it will be easier for u to understand.. once its done come back here, it will be more easier..! Now, let's go back to topic first question as usual, let's question the assumptuons

Is This Even a CloudFront Problem?

Jake runs a small phone shop, and he updates his shop's "Trade-In Values" PDF every Monday morning. He uploads the new file to S3, replacing the old one at the same path. His customers keep pulling up last week's prices on their phones, arguing about a valuation that's a week stale. That's a real cost: he either honors the old number and loses money, or he argues with a customer holding a screenshot.

Before touching CloudFront at all, it's worth asking where the staleness actually lives, because there are three separate caches between "I uploaded a new file" and "the customer sees it": the visitor's own browser cache, CloudFront's edge cache, and, in rare setups, a caching layer in front of S3 itself. Only one of those three is CloudFront, and invalidating CloudFront does nothing at all to the other two.

‍♂️ Jake's Reality Check

"I invalidated the file, waited five minutes, reloaded the page, and it's still the old PDF. Did the invalidation just not work?"

Probably not — your browser is likely serving its own cached copy, not CloudFront's. A normal page reload (Ctrl+R or clicking reload) can reuse the browser's local cache instead of asking CloudFront again. A hard reload, or opening the URL in a private/incognito window, forces a real request past the browser cache and out to the edge.

So the diagnostic order is: confirm CloudFront is actually serving stale content (not your browser), invalidate if it is, and then decide whether you want to keep manually invalidating every week or fix the underlying cache settings so this stops happening. Ethan, who's spent enough years arguing with edge caches to have opinions about it, puts it bluntly: "Invalidation is a fire extinguisher. If you're reaching for it every week, you don't have a fire problem, you have a smoke-detector-wired-to-the-stove problem."

Check the X-Cache Header Before You Touch Anything

CloudFront tells you exactly what happened on every response, through the X-Cache header. You don't need any tooling beyond a terminal:

curl -sI https://d1234abcdef.cloudfront.net/trade-in-values.pdf | grep -i 'x-cache\|age'

X-Cache: Hit from cloudfront with a rising Age value (the number of seconds since the object was fetched from the origin) means CloudFront is still serving the cached copy — that's your confirmation, and it's what an invalidation is for. X-Cache: Miss from cloudfront means CloudFront just went back to S3 for a fresh copy; if the file is still wrong after a Miss, the problem isn't CloudFront's cache at all — it's the file in S3 itself, or you're pointed at the wrong bucket, key, or origin path.

This single check saves a lot of wasted invalidation requests. Don't invalidate a path and assume it worked because the page "looks fixed" — confirm the header actually flips from Hit to Miss to Hit-with-a-low-Age on the next request. That's the difference between having fixed it and having refreshed your own browser cache while the edge cache never changed.

Route 1: Create an Invalidation From the Console

An invalidation is a request that tells CloudFront "stop serving your cached copy of these paths, and go back to the origin the next time anyone asks for them." It doesn't touch anything in S3 — your origin file is untouched either way; invalidation only clears CloudFront's copy of it. For a one-off fix, the console is the fastest route:

  1. Sign in to the AWS Management Console and open the CloudFront console.
  2. Choose the distribution serving the stale file.
  3. Open the Invalidations tab.
  4. Choose Create invalidation.
  5. Enter one invalidation path per line — for example /trade-in-values.pdf, or /images/* to cover everything under a folder.
  6. Choose Create invalidation to submit it.

A few things about the paths themselves matter enough to trip people up. The wildcard character can only go at the end of a path — an asterisk placed anywhere else in the string is treated as a literal character, not a wildcard, so /images/*.jpg does not do what it looks like it does. The maximum length of a single path is 4,000 characters, and CloudFront doesn't support the ~ character in an invalidation path at all, even URL-encoded. And once you submit a request, you can't cancel it — specify the paths carefully before you click through.

The console shows the invalidation's status, which starts as InProgress and changes to Completed once it's finished propagating across CloudFront's edge locations. That's usually a matter of minutes, though a heavily used distribution can take a little longer.

✅ Why the console is the right call for a one-off fix

If this is a "the trade-in PDF is wrong right now" situation and you're not going to repeat it in the next five minutes, the console is faster than opening a terminal, configuring credentials, and remembering the distribution ID. Save the CLI and boto3 for anything you'll do more than once.

Command Line and boto3: Invalidating From Scripts and Pipelines

For anything you'll do more than once — a deploy script, a scheduled job, a Lambda that reacts to an S3 upload — the AWS CLI or the SDK is the right tool. The CLI equivalent of the console flow:

aws cloudfront create-invalidation \ --distribution-id E1A2B3C4D5E6F7 \ --paths "/trade-in-values.pdf" "/images/*"

Quote any path that contains a wildcard — the CLI needs the quotes around "/*" so your shell doesn't try to expand the asterisk itself against local files. To check on it or wait for it programmatically:

aws cloudfront get-invalidation --distribution-id E1A2B3C4D5E6F7 --id I1234567890 aws cloudfront wait invalidation-completed --distribution-id E1A2B3C4D5E6F7 --id I1234567890

The wait subcommand is what you want inside a deploy script, so the pipeline doesn't move on to the next step (a smoke test, a notification) before the invalidation has actually finished.

In Python, the boto3 SDK does the same thing through the CloudFront client. The one field that trips people up is CallerReference — CloudFront requires a unique value on every invalidation request so it can tell a retry apart from a brand-new request:

import boto3 import uuid client = boto3.client('cloudfront') response = client.create_invalidation( DistributionId='E1A2B3C4D5E6F7', InvalidationBatch={ 'Paths': { 'Quantity': 2, 'Items': ['/trade-in-values.pdf', '/images/*'] }, 'CallerReference': str(uuid.uuid4()) } ) invalidation_id = response['Invalidation']['Id'] print(invalidation_id)

If you submit a second request with the exact same CallerReference as an earlier one, and the paths match, CloudFront won't create a new invalidation — it just hands back the original one. That's intentional: it protects a retried network call from accidentally submitting the same invalidation twice. If the paths are different but the CallerReference matches an earlier request, CloudFront returns an InvalidationBatchAlreadyExists error instead, which is your signal to generate a new reference value (a UUID, as above, or a timestamp) rather than reusing one.

Invalidation Limits, Cost, and the TooManyInvalidationsInProgress Error

Invalidation isn't unlimited, in two separate ways: how many can be running at once, and what it costs. Both matter more than they look like they should if you're calling it from a loop.

Quota Limit What happens if you exceed it
Individual (non-wildcard) file paths in progress Up to 3,000 per distribution, at once, in any combination TooManyInvalidationsInProgress error until earlier ones finish
Wildcard paths in progress Up to 15 per distribution, at once (a separate, independent quota) Same error — wait for existing wildcard invalidations to complete
Free invalidation paths First 1,000 paths per month, per AWS account, across all distributions $0.005 per path beyond 1,000 — billed, not blocked

Two details in that table change how you should script this. First, a wildcard path like /images/* counts as exactly one path against both the free-tier allowance and the concurrency limit, no matter how many actual files it matches — which is why teams that need to clear a lot of files reach for one wildcard instead of listing every file individually. Second, the 3,000-file concurrency limit is a shared pool: it can be one request for 3,000 files, 30 requests for 100 files each, or any other combination, and if you're still processing 3,000 files' worth of in-progress invalidations, the 3,001st file-level request fails until something finishes.

⚠️ What actually breaks: the per-file loop

If your deploy script calls create-invalidation once per changed file instead of batching them into one request (or one wildcard), you'll burn through both the 3,000-file concurrency quota and the 1,000-path free allowance far faster than the file count alone would suggest — every individual path is billed and counted separately once you're past the free tier, at $0.005 each. Batch your paths into a single InvalidationBatch call per deploy.

Route 2: Fix the Root Cause With Cache-Control and TTL

Invalidation fixes today's problem. It doesn't stop next Monday's version of the same problem. The actual cause of "CloudFront still serves the old file" is that CloudFront was told, explicitly or by default, to hold onto that object for some number of seconds — the object's Time to Live. Understanding how TTL gets decided is what lets you stop invalidating manually.

If your distribution uses the AWS-managed CachingOptimized cache policy — the common default for a static site or S3-backed distribution — its TTL settings are: Minimum TTL 1 second, Default TTL 86,400 seconds (24 hours), Maximum TTL 31,536,000 seconds (365 days). How those three numbers interact with whatever your S3 object's own Cache-Control header says determines how long the file actually sits at the edge:

What the origin sends What CloudFront does
No Cache-Control or Expires header at all Falls back to the policy's Default TTL — 86,400 seconds on CachingOptimized
A caching header shorter than the Minimum TTL Uses the Minimum TTL instead — the origin's shorter value is overridden upward
A caching header longer than the Maximum TTL Caps at the Maximum TTL — the origin's longer value is overridden downward

That middle row matters more than it looks like. If your cache policy's Minimum TTL is greater than zero, CloudFront will cache the object for at least that long even when your origin sends Cache-Control: no-cache, no-store, or private. Those directives, which normally mean "don't cache this," get overridden by a nonzero Minimum TTL. This is one of the few places where you genuinely can't fix it from the S3 side alone — if a Minimum TTL greater than zero is set on the cache policy, no header you attach to the object will make CloudFront skip caching it; you have to lower the Minimum TTL on the policy itself, or set all three TTL values to 0 to disable caching for that behavior entirely.

Setting Cache-Control on the S3 Object

Since Jake's trade-in PDF changes every Monday and isn't a versioned filename, the practical fix is a short max-age on that specific object, so CloudFront checks back with S3 far more often than once a day without needing a manual invalidation at all. To edit it for one existing object in the console:

  1. Open the S3 console and go to the bucket containing the object.
  2. Select the checkbox next to the object (or the folder, to edit several at once).
  3. Choose Actions, then Edit metadata.
  4. Add or edit the Cache-Control key, with a value such as max-age=300 for a five-minute cache.
  5. Save the change.

Editing metadata this way creates a new version of the object if S3 Versioning is enabled, or replaces it in place if not; either way, the AWS account performing the edit becomes the object's owner. For files you upload regularly rather than editing one at a time, set it at upload time instead:

aws s3 cp trade-in-values.pdf s3://your-bucket/trade-in-values.pdf \ --cache-control "max-age=300"

The general shape recommended for a static site fronted by CloudFront: give HTML and any file without a version identifier in its name a short max-age, since those are exactly the files that change without a URL change, and give versioned or hashed assets (app.a3f9b2.js, for instance) a long max-age, since a new version simply gets a new filename and can't ever go stale under an old one. That second half is the real escape hatch: a hash-versioned filename doesn't need invalidation, ever, because updating the file means the URL changes too, and CloudFront has simply never seen that URL before.

Cache Tag Invalidation: Skipping the Path List Entirely

If you don't just have one PDF, but dozens of files scattered across different paths that all logically belong to "the same thing" (every asset tied to a particular product, or every page for a particular user), CloudFront supports invalidating by cache tag instead of by listing every path. Your origin attaches an x-amz-meta-cache-tag header to the response with one or more comma-separated tags, and you invalidate everything carrying a given tag with a #-prefixed path:

aws cloudfront create-invalidation \ --distribution-id E1A2B3C4D5E6F7 \ --paths "#product:trade-in-values"

Tag-based invalidation is opt-in: your distribution needs CacheTagConfig configured before CloudFront will act on those tag headers at all — a distribution without that configuration simply ignores the tag header, so this isn't a drop-in replacement for path invalidation without first turning it on. Where it's set up, though, one tag invalidation can clear objects across many different URL paths in a single request. Tag invalidation paths count toward the same 1,000-free-paths-per-month allowance as ordinary path invalidations, and a single tag counts as one path the same way a wildcard does, regardless of how many tagged objects it actually clears.

Which Method Should You Actually Use?

Jake's question after reading this far was fair: "So which one of these am I actually supposed to use on Monday morning?" The honest answer is that most sites end up using two or three of these together, not one. Here's how they stack up against each other:

Method Fixes it right now? Prevents it next time? Best for
Console invalidation Yes No A rare, one-off manual fix
CLI / boto3 invalidation Yes No (unless scripted into every deploy) Repeatable fixes, scheduled jobs, CI/CD steps
Cache tag invalidation Yes, once opted in No Clearing many scattered paths that share a logical grouping
Shorter Cache-Control / TTL No — waits out the new TTL Yes, going forward Files that change on a predictable schedule, like Jake's PDF
Versioned / hashed filenames Yes — the new file is a new URL Yes, entirely Build assets, anything a deploy pipeline generates

Ethan's opinion, and he'll say it's not really a controversial one: "Manual invalidation is what you reach for the first time this happens. Fixing the Cache-Control header, or moving to versioned filenames, is what you do so it stops being a recurring task on your calendar." For Jake's specific case — a single non-versioned file that changes on a fixed weekly schedule — a short max-age is the better long-term answer than remembering to invalidate every Monday; for a full site redeploy touching dozens of build files, versioned filenames plus one wildcard invalidation for the handful of files that can't be versioned (like the root index.html) covers both cases at once.

The Hard Cases: When Invalidation Completes but Nothing Changes

Sometimes the invalidation shows Completed, X-Cache confirms a fresh Miss, and the file is still visibly wrong. At that point the cache layer isn't the problem anymore — something upstream of it is. In order of how often each one turns out to be the actual cause:

The wrong object was uploaded, or to the wrong key. Fetch the object directly from S3 (not through CloudFront) and confirm it's actually the new version at the exact key the distribution's origin path expects. It's an easy step to skip, and it's the single most common false alarm.

Query strings are part of the cache key, and the URL you're testing doesn't match the one you invalidated. If your cache policy includes query strings in the cache key, /data?version=1 and /data?version=2 are cached as entirely separate objects — invalidating /data alone won't touch either of them; you'd need to invalidate the specific query-string variant, or invalidate the path with a trailing wildcard if that's how your policy treats it.

You're testing the wrong distribution or the wrong CloudFront domain. If your setup involves more than one distribution behind the same custom domain (a staging distribution and a production one, for instance), you can invalidate the one you're not actually hitting.

Something's caching in front of CloudFront, like a corporate proxy or a local DNS resolver holding an old IP. Rare, but worth ruling out with a request from a different network before assuming the fix itself is broken.

And the one Ethan says he sees most from people newer to CloudFront: "They invalidate, it's still stale, so they invalidate again five more times in a panic. All that does is eat into the free 1,000-path allowance and burn through the 15-in-progress wildcard limit for nothing — the second, third, and fourth invalidation of the same unchanged path don't do anything a completed first one hadn't already done."

Edge Cases: When CloudFront Functions or Lambda@Edge Are in the Mix

If your distribution runs a CloudFront Function or a Lambda@Edge function on the viewer request, there's an extra layer to check before you conclude an invalidation "didn't work." Both of these let you rewrite the request before CloudFront decides what's cached under which key — that's literally one of their listed uses, described as cache key normalization: transforming headers, query strings, cookies, and even the URL path itself to build the key CloudFront actually caches against.

Jake doesn't run anything this fancy for his phone shop site, but Ethan has seen the trap enough times to warn for it anyway: "If a function rewrites the URI — say, stripping a trailing slash or redirecting by country — changing that value changes what object the viewer is actually requesting, but it doesn't change which cache behavior or origin the request goes to. So if you invalidate the path the visitor typed in their browser, and a function silently rewrote it to something else before CloudFront cached it, you invalidated the wrong key." The fix is to invalidate the path as it exists after the function rewrites it, not the one in the address bar.

A second, subtler trap: including extra values in the cache key — a header like Accept-Language, for instance — means CloudFront can end up caching multiple variants of what looks like the same URL, one per distinct header value it sees. That's often intentional and useful, but it does mean a path-based invalidation clears every variant cached under that path, which is usually what you want, but it's worth knowing if you're trying to reason about exactly what got cleared and what didn't.

Automating Invalidation in a Deploy Pipeline Without Overspending

For a CI/CD pipeline that syncs a build to S3 on every deploy, the pattern that keeps both the concurrency quota and the free-tier allowance sane is: sync the files first, then submit exactly one invalidation request per deploy covering everything that changed, rather than one request per file.

aws s3 sync ./build s3://your-bucket/ --delete aws cloudfront create-invalidation \ --distribution-id E1A2B3C4D5E6F7 \ --paths "/*"

A single /* counts as one path against the free allowance and one slot against the 15-wildcard-in-progress limit, no matter how many files the sync touched — which is precisely why teams reach for the wildcard over enumerating every changed file individually in a script. The tradeoff, and it's a real one, is that a broad /* clears cached objects that didn't actually change, forcing CloudFront to re-fetch all of them from the origin on the next request rather than just the handful that were updated. For a deploy where most of the build output is versioned/hash-named JS and CSS that never needed invalidating in the first place (see the Cache-Control section above), that tradeoff barely matters, because those files' URLs never repeat. For a deploy where most files share stable, unversioned names, a narrower wildcard scoped to just the changed directory keeps the origin from being hammered with unnecessary re-fetches.

Third-Party Tools and CI/CD Actions — And When Not to Bother

Most CI/CD platforms have prebuilt actions or pipes that wrap the exact two commands above — sync to S3, then invalidate CloudFront — into a single reusable step, and a few go further with a "smart" mode that tries to submit fewer, narrower invalidation paths instead of defaulting to a blanket /* on every run. If you're already deploying through GitHub Actions, Bitbucket Pipelines, or a similar platform, it's worth checking whether one exists before hand-rolling the two commands yourself, since it also handles credential wiring for you.

That said, Jake genuinely doesn't need any of this. His deploy is one file, once a week, uploaded by hand. Wiring a CI/CD action, storing AWS credentials in a pipeline, and maintaining that configuration is more overhead than the problem justifies — the two-line CLI command from the Automation section above, run manually or from a basic scheduled task, does the same job with far less to maintain. Reach for a packaged action once deploys are frequent enough, or numerous enough across environments, that doing it by hand has become the actual bottleneck; not before.

The Privacy Angle: Don't Cache What Shouldn't Be Public

Everything above assumes the file in question is meant to be public. Before you set a long max-age on something to reduce how often you need to invalidate it, it's worth double-checking that the object doesn't carry information that shouldn't be cached at the edge for anyone holding the URL — a customer list export, a signed contract PDF, anything gated behind a login on your own site but reachable by direct link.

CloudFront has a dedicated mechanism for this: signed URLs and signed cookies, which only grant access for a specified window of time, combined with an origin access control (OAC) on the S3 bucket so the file can't be fetched directly from its S3 URL, bypassing CloudFront's access checks entirely. If that's not set up, a long Cache-Control: max-age on a sensitive object means CloudFront (and any downstream cache, and the visitor's own browser) will happily keep serving it to anyone who has the link, for as long as the TTL says to, whether or not your application's own login check would have blocked them.

The practical takeaway for anything with access implications: keep the TTL short regardless of how static the content otherwise is, and treat "I invalidated it" as your only lever to immediately pull a wrongly-cached copy back — which is one more reason the free 1,000-path invalidation allowance and the console's speed matter beyond convenience.

Frequently Asked Questions

What is cache invalidation, exactly?

It's a request that tells CloudFront to discard its cached copy of specific paths and go back to your origin for a fresh copy the next time someone requests them. It doesn't change anything in S3; it only clears CloudFront's own edge copy.

Why does CloudFront serve stale content in the first place if S3 already changed?

Because CloudFront doesn't check back with S3 on every request — that's the entire point of a CDN cache. It serves the copy it already has at the edge for as long as the object's TTL says to, based on the object's Cache-Control header (or the policy's Default TTL if there isn't one), and only re-fetches from S3 once that TTL runs out or you force it with an invalidation.

How long does a CloudFront invalidation take to complete?

Typically a few minutes, tracked by status changing from InProgress to Completed. A distribution under heavy load can take a bit longer, but there's no fixed guaranteed duration published for it.

Does invalidating the cache cost anything?

The first 1,000 invalidation paths you submit per month are free, counted per AWS account across every distribution you own. Past that, each additional path is $0.005. A wildcard path like /* still only counts as one path, no matter how many files it clears.

Can I cancel an invalidation after I submit it?

No. Once you choose Create invalidation, the request runs; there's no cancel option, so it's worth double-checking the paths before submitting, especially a broad wildcard.

Why do I still see the old version after invalidation completes?

Most often it's the browser's own local cache serving the old copy rather than a real request reaching CloudFront — a hard reload or a private window rules that out. If a hard reload still shows the old file, check the X-Cache header directly with curl; a fresh Miss with the wrong content means the problem has moved upstream to the origin object itself, not the cache.

Does invalidation clear the visitor's browser cache too?

No. Invalidation only affects CloudFront's edge cache. A visitor's browser cache is controlled by the same Cache-Control header, but the browser decides independently how to honor it, and CloudFront invalidating its own copy has no effect on a copy already sitting in someone's browser.

What's the difference between /images/* and /images/logo.jpg as an invalidation path?

/images/logo.jpg invalidates exactly that one file. /images/* invalidates every cached object under that path, and still counts as a single path for both billing and the concurrency quota — useful when several files under a folder changed and you don't want to list each one.

How many invalidations can I run at once?

Up to 3,000 individual file paths in progress at a time per distribution, in any combination of requests, and separately, up to 15 wildcard paths in progress at a time. Exceed either and you'll get a TooManyInvalidationsInProgress error until earlier requests finish.

Why did my invalidation fail with "TooManyInvalidationsInProgress"?

You've hit one of the two concurrency quotas above — usually the 15-wildcard-in-progress one, if a script is submitting a fresh /* request on every retry without waiting for the previous one to complete. Batch requests into fewer, larger ones, or wait for existing invalidations to finish before submitting more.

Do I need to invalidate CloudFront every time I redeploy?

Only for files whose URL doesn't change between versions. Hash-versioned or content-addressed filenames sidestep the need entirely, since a changed file gets a new URL that CloudFront has never cached anything under.

How do I invalidate CloudFront cache using boto3?

Call client.create_invalidation() with the distribution ID, a Paths dict containing the paths and their count, and a unique CallerReference string (a UUID or timestamp works). Reusing the same CallerReference with different paths returns an InvalidationBatchAlreadyExists error, so generate a new one on every call.

What does X-Cache: Miss from cloudfront mean?

It means CloudFront didn't have a valid cached copy and went to the origin for a fresh one on that request — either because nothing was cached yet, the TTL had already expired, or an invalidation just cleared it. If the content is still wrong after a confirmed Miss, the issue is upstream of CloudFront's cache.

Can I invalidate by tag instead of listing every path?

Yes, if your distribution has cache tag invalidation configured (via CacheTagConfig) and your origin attaches an x-amz-meta-cache-tag header to responses. You then invalidate with a #-prefixed tag name instead of a URL path, and it clears every object carrying that tag regardless of where it lives.

Does setting a shorter Cache-Control on S3 fix this permanently?

For files without version identifiers in the name, yes — a short max-age means CloudFront checks back with S3 on a schedule you control, instead of holding the default 24-hour TTL. It won't help if your cache policy's Minimum TTL is set above zero, since that overrides even a no-cache header from the origin; in that case the fix is on the cache policy, not the object.

Is there a faster way than invalidation for files I update often?

Yes: give the file a version-identifying filename (a content hash or a version number in the path) instead of overwriting the same name each time. A new filename is, to CloudFront, simply a URL it's never cached anything under, so it's served fresh on the very first request with no invalidation step at all.

Revision note. Written August 2026, covering CloudFront's current invalidation console and CLI flow, the AWS-managed CachingOptimized policy's TTL defaults, cache-tag invalidation, cache-key interactions with CloudFront Functions and Lambda@Edge, and current invalidation pricing and concurrency quotas. If AWS changes the free invalidation allowance, the concurrency limits, or the managed policy's default TTL, the numbers above will need updating. If you're the one standing at the counter arguing about a stale price with a customer right now: run the invalidation from the Quick Answer box first, then come back and read the rest when things are calmer.

Related