Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

arjansingh
Copy link
Contributor

Overview

Fixes Comfy-Org/ComfyUI_frontend#4438

Majority of discussion was in:

  1. [Bug]: Model sidebar makes unnecessary 404 requests for non-existent preview images Comfy-Org/ComfyUI_frontend#4438
  2. [fix] fix #4438 do not repeatedly try to load 404'ed images Comfy-Org/ComfyUI_frontend#5174 and

TL;DR: Add cache controls to store images for 1 day and only try to resolve missing images every 1 hour.

Problem

  1. model_manager.py has an experimental preview endpoint to serve preview images from the filesystem.
  2. frontend code tries to fetch images these images, but there is no cache control on them so we repeatedly fetch and download them.
  3. This causes unnecessary traffic to the server and a lot of network traffic noise in development.
  4. We fix this by adding caching logic for images in our middleware.

Review Points

  1. Are we okay with the default max-age values? One day for images and permanent redirects. One hour for image 404's.

server.py Outdated
Comment on lines 65 to 69
elif 200 <= response.status < 300:
response.headers.setdefault('Cache-Control', f"public, max-age={ONE_DAY}")
elif response.status == 301 or response.status == 308:
# Permanent redirects - cache for 1 day
response.headers.setdefault('Cache-Control', f"public, max-age={ONE_DAY}")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
elif 200 <= response.status < 300:
response.headers.setdefault('Cache-Control', f"public, max-age={ONE_DAY}")
elif response.status == 301 or response.status == 308:
# Permanent redirects - cache for 1 day
response.headers.setdefault('Cache-Control', f"public, max-age={ONE_DAY}")
elif 200 <= response.status < 300 or response.status == 301 or response.status == 308:
# Permanent redirects - cache for 1 day
response.headers.setdefault('Cache-Control', f"public, max-age={ONE_DAY}")

server.py Outdated
Comment on lines 60 to 62
if response.status == 304:
# 304 Not Modified - don't set cache headers, inherit from original
pass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be an and response.status != 304 on line 70?

server.py Outdated
response.headers.setdefault('Cache-Control', f"public, max-age={ONE_DAY}")
elif 300 <= response.status < 400:
# Temporary redirects (302, 303, 307) and other 3xx - no cache
response.headers.setdefault('Cache-Control', 'no-cache')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just know there's a way to unify this with line 58...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good instincts, as usual. I'm missing the pattern with line 58. Could you show me what you're seeing there?

Copy link
Contributor Author

@arjansingh arjansingh Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@web.middleware
async def cache_control(request: web.Request, handler: Callable[[web.Request], Awaitable[web.Response]]) -> web.Response:
    response: web.Response = await handler(request)
    if request.path.endswith('.js') or request.path.endswith('.css') or request.path.endswith('index.json'):
        response.headers.setdefault('Cache-Control', 'no-cache')
    elif request.path.lower().endswith(IMG_EXTENSIONS):
        if response.status == 404:
            response.headers.setdefault('Cache-Control', f"public, max-age={ONE_HOUR}")
        elif response.status in (200, 201, 202, 203, 204, 205, 206, 301, 308):
            # Success responses and permanent redirects - cache for 1 day
            response.headers.setdefault('Cache-Control', f"public, max-age={ONE_DAY}")
        elif response.status in (302, 303, 307):
            # Temporary redirects - no cache
            response.headers.setdefault('Cache-Control', 'no-cache')
        # Note: 304 Not Modified falls through - no cache headers set
    return response

Something like this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

response.headers.setdefault('Cache-Control', 'no-cache')

and

response.headers.setdefault('Cache-Control', 'no-cache')

I'd love for each version to occur exactly once.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm gonna push back on that last one.

We have clearly defined rules for js and css and images.

I don't think it makes sense to commingle rules for assets. just so that we only set no-cache once.

Better to optimize by asset type than by cache level here. Makes it easier to tweak just images as well.

Comment on lines 10 to 12
with patch('app.frontend_management.FrontendManager'):
with patch('utils.install_util.get_missing_requirements_message'):
with patch('utils.install_util.requirements_path'):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if this is better, but it's an option

Suggested change
with patch('app.frontend_management.FrontendManager'):
with patch('utils.install_util.get_missing_requirements_message'):
with patch('utils.install_util.requirements_path'):
with patch('app.frontend_management.FrontendManager'), patch('utils.install_util.get_missing_requirements_message'), patch('utils.install_util.requirements_path'):

server.py Outdated
response.headers.setdefault('Cache-Control', f"public, max-age={ONE_DAY}")
elif 300 <= response.status < 400:
# Temporary redirects (302, 303, 307) and other 3xx - no cache
response.headers.setdefault('Cache-Control', 'no-cache')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

response.headers.setdefault('Cache-Control', 'no-cache')

and

response.headers.setdefault('Cache-Control', 'no-cache')

I'd love for each version to occur exactly once.

pytestmark = pytest.mark.asyncio # Apply asyncio mark to all tests

# Mock the problematic imports before importing server
with patch('app.frontend_management.FrontendManager'), patch('utils.install_util.get_missing_requirements_message'), patch('utils.install_util.requirements_path'):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DrJKL this was a good call by you. much easier to read.

@arjansingh
Copy link
Contributor Author

Hold up. Need to refactor the middleware so we don't get stuck in mock hell with pytorch.

@arjansingh arjansingh force-pushed the fix/add-image-cache-headers branch from 7f0e91e to 7cd6891 Compare August 29, 2025 19:35
Comment on lines 29 to 46
if (
request.path.endswith(".js")
or request.path.endswith(".css")
or request.path.endswith("index.json")
):
response.headers.setdefault("Cache-Control", "no-cache")
elif request.path.lower().endswith(IMG_EXTENSIONS):
if response.status == 404:
response.headers.setdefault("Cache-Control", f"public, max-age={ONE_HOUR}")
elif response.status in (200, 201, 202, 203, 204, 205, 206, 301, 308):
# Success responses and permanent redirects - cache for 1 day
response.headers.setdefault("Cache-Control", f"public, max-age={ONE_DAY}")
elif response.status in (302, 303, 307):
# Temporary redirects - no cache
response.headers.setdefault("Cache-Control", "no-cache")
# Note: 304 Not Modified falls through - no cache headers set

return response
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Talked about elsewhere: This is probably more flattenable.

server.py Outdated
except (aiohttp.ClientError, aiohttp.ClientPayloadError, ConnectionResetError, BrokenPipeError, ConnectionError) as err:
logging.warning("send error: {}".format(err))

@web.middleware
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line should be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. My bad.

@comfyanonymous comfyanonymous merged commit 3493b9c into comfyanonymous:master Sep 5, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Model sidebar makes unnecessary 404 requests for non-existent preview images

3 participants