Skip to content


COMPRESS_PRECOMPILERS break css url() #226

domoritz opened this Issue · 37 comments

Since the precompiled files are served from another location, relative links in css files do not work anymore.

The Problem can be solved by setting the output dir to an empty string.


However, it would be great to have processed urls in the css file that link to the right locations of images.


I'm not sure I understand correctly, can you provide a test case showing the problem?


I have a scss precompiler for my scss files. It has to be executed even in development since the browser can't handle scss files.

    ('text/x-scss', 'pyscss -C {infile}'),

As a result I get a precompiled css for each scss file. So far so good. however, the problem occurs when I reference images with url() inside my scss files.Since these are not processed, relative urls, such as ../images/foo.png won't work anymore because the precompiled css files are served from another location and the images cannot be accessed. The problem is for example that urls will be relative to the CACHE location and not the static location in case you are using django.contrib.staticfiles.

My config looks like this:

DEBUG = True
STATIC_URL = '/static/'
    # other finders..

this works:


this does not:


Yes, this is an issue (at least, I have the same problem). I'm using twitter bootstrap as a css framework. That contains less stylesheets and I handle those with compressor.

But the sprite file with icons that's included in twitter bootstrap is missing when setting COMPRESS_ENABLED=False (as I'm developing) because the icon .png file isn't moved to the CACHE/ directory.


It would be a solution to serve all static content from the CACHE location while DEBUG=True.


Well, for the record, that's why the CssAbsoluteFilter exists, to convert relative URLs to absolute ones. If you don't want to use it, it's perfectly fine to set COMPRESS_OUTPUT_DIR to an empty string. Serving files from that directory is out of scope of compressor.

@jezdez jezdez closed this

CssAbsoluteFilter does not run when COMPRESS_ENABLED = False.

In my use case I want to run the precompressor and the CssAbsoluteFilter, but I don't want to compress the css.
This is not possible since in enabled = settings.COMPRESS_ENABLED or forced
And self.filters where is not called because enabled = False.

Is it possible to use CssAbsoluteFilter without compressing the css?


It's not possible right now, no.


I've encountered this issue as well, and it makes development a bit troublesome. Setting the COMPRESS_OUTPUT_DIR to an empty string might work, but only in simple cases; any sort of deeper directory structure breaks all of the url paths in CSS files, since the pre-compilers output to COMPRESS_OUTPUT_DIR/css regardless of source location. With so many css urls being relative, you're forced to turn compression on or abandon pre-compilers.

One alternative I was brainstorming, was detecting in the file to be pre-compiled, whether or not compression is enabled and adjusting the image paths in that file...which would be an ugly hack if it was possible.


Scratch my comment. It does still apply, but I think one of the pull requests (specific commit below), will allow for the CssAbsoluteFilter, thus avoiding the issue entirely. Nice.


@jezdez jezdez reopened this

Setting COMPRESS_OUTPUT_DIR to an empty directory just removes CACHE from the resulting filename. So the file still ends up in the COMPRESS_ROOT.

Setting COMPRESS_ROOT to the empty string places CACHE/css/... in the directory where you started django.

So there's no way to currently get django-compressor to create the output file in the same directory as the source file.


Same problem here, I think it is important


My workaround for this was to add the following to the bottom of my settings:

    COMPRESS_CSS_FILTERS = ['compressor.filters.css_default.CssAbsoluteFilter']

The settings are saying don't apply filters, but you want this one to apply anyway. You want some filters to get some sort of special status that makes them run whether or not COMPRESS_ENABLED is true. I imagine an attribute on the relevant filter's class might be able to tell django-compressor to always use it. This isn't ideal, because the developer won't expect things to be changed. The above mentioned approach of attaching filters to precompiler definitions might be nicer.


I'm doing the same as @willhardy at the moment. It works, but as the compressor runs, the JS files also are concatenated and that makes the debugging a bit harder, as the resulting file can be quite long.

It should be possible to run some filters even when COMPRESS_ENABLED = False. Or should the CssAbsoluteFilter be run as a precompiler, as it really doesn't do any compressing? The pull request #291 is somewhat related, as it would allow class-based precompilers.


Sorry to leave a "me too" comment; but I too am doing the same as @willhardy. It would be nice not to have JS or CSS files concatenated while debugging; and it's also nice if SASS/SCSS/LESS files are compiled into something with a filename that indicates the source.


+1 on this bug. Took me a while to understand this, but here is the problem.

<link type="text/less" href="/static/less/main.less">

.myclass {
    background: url(../img/myimg.png);

The above attempts to reference this location: /static/img/myimg.png And this would work, if the generated css was served from /static/css/main.css. However, the COMPRESSOR_OUTPUT_DIR = 'CACHE' ends up adding a directory level because something that gets precompiled gets served from the COMPRESSOR_OUTPUT_DIR.

So, the above less gets precompiled and served from:
<link type="text/less" href="/static/CACHE/css/main.css"

Which ends up looking in the full path: /static/CACHE/img/myimg.png which is pretty much guaranteed to not exist.

So, this is actually a special case for css IMO. A CSS Absolute filter is an absolute requirement when it comes to precompilers only because of where a precompiled file is served from.

This problem gets compounded with the current absolute filter. Trying to account for the CACHE directory in main.less ends up breaking things very bad.

.myclass {
    background: url(../../img/myimg.png);

The above attempts to account for the extra CACHE dir, but when CSS Absolute Filter comes through, it'll make the full path: /img/myimg.png (notice no /static prefix) which is actually correct given the path to the less file /static/less/main.less. However, it's not given the files final resting place in the CACHE directory.

Hopefully I've helped clear up any misunderstandings if there were any.


I have just submitted a pull request to address this issue, which is a big one for me too.


I just started using SCSS with Django Compressor and this is a big problem for me also.

I'm using @willhardy's hack at the moment but it creates issues with when the value of COMPRESS_ENABLED is changed in a local settings file.


Can the absolute URL filter just be implicit, and disabled by an optional setting? e.g.


in the rare instance someone wouldn't want this functionality.


+1, having this issue myself as well


I'm currently using a custom Css absolute filter + SCSS precompiler combination to get around this issue and it works without any problems. The main point here is that the CssAbsoluteFilter is used during precompilation, so I don't need to enable compression for it to work. This keeps the original SCSS/JS files separate and the names of the files are not obfuscated hashes, which makes for happier development :-)

(The implementation is adapted from somone's LESS-based implementation, but I can't find it right now. Anyhow, the code is very easy to convert to LESS if that's your preprocessor choice.)

In lib/

from compressor.filters.base import CompilerFilter
from compressor.filters.css_default import CssAbsoluteFilter

class SCSSFilter(CompilerFilter):
    def __init__(self, content, attrs, **kwargs):
        super(SCSSFilter, self).__init__(content, command='scss {infile} {outfile}', **kwargs)

    def input(self, **kwargs):
        content = super(SCSSFilter, self).input(**kwargs)
        return CssAbsoluteFilter(content).input(**kwargs)

and in settings

    ('text/scss', 'lib.scssabsolutefilter.SCSSFilter'),

Anyway, it would be nice if there was a way to do this correctly without any extra code.


+1 for this issue and I'm not even using any LESS or SASS
Simply relative url in CSS file and I have to pick between "../../images/" for production to work or "../images" for development to work.
Well I picked for production to work of course then debugging is hard and localhost looks bad.


Also I'm on Amazon S3 so I cannot make a sim link like suggested at yourcelf/btb#20


Same issue here. django-cms, django-sekizai, django-compressor and pyscss.

@andrefsp andrefsp pushed a commit to andrefsp/django-oscar that referenced this issue
@codeinthehole codeinthehole Change compressor settings to fix relative URL issue
When the LESS files are served but compression is disabled, the relative
URL between the generated CSS and other assets is wrong (using the
default compressor settings).  This fixes changes the output directory
name to match the path of Oscar's assets, which fixes this issue.

Related to django-compressor/django-compressor#226

Fixes #649

Same issue here.

CssAbsoluteFilter should be a compressor option, as @dustinfarris said (which could be implemented as a filter internally). You already know that compressor will generate a css in a different location than the original file, so it is very likely that your relative paths are going to break. When would you not want to fix your relative paths?


Not elegant at all, but I solved this by creating a couple of symlinks.


Is this still open, really? How do people develop locally using sass/less/scss? I believe this is a very important bug.


I'm using a customer LessCompiler that runs the output through the CssAbsoluteFilter, similar to @anttihirvonen, and this works pretty well.

Another solution is to use absolute urls in your LESS files, with a @static-url variable that matches django's STATIC_URL setting. You won't be needing CssAbsoluteFilter anymore, but this may not work if you are using S3. For example:
background: url("@{static-url}/img/bg.png");


+1, @anttihirvonen's solution works, but IMO it should be possible to do this through settings only.


I've modified @anttihirvonen's solution to work with django-libsass (which uses the faster C implementation of SASS):

from compressor.filters.css_default import CssAbsoluteFilter
from django_libsass import SassCompiler

class PatchedSCSSCompiler(SassCompiler):
    def input(self, **kwargs):
        content = super(PatchedSCSSCompiler, self).input(**kwargs)
        return CssAbsoluteFilter(content).input(**kwargs)

It would be nice, though, I've there wasn't such a workaround needed. And it took my quite a while to figure out what was going on and how to fix it.


@woeye Having just stumbled across this problem myself, I propose to incorporate this workaround into django-libsass: torchbox/django-libsass#8

+1 for a general solution to this within django-compressor, though!


With django-compressor 1.5:
the fix for #467 (db731cf) changed the CSSAbsoluteFilter, which breaks the 'PatchedSCSSCompiler' workaround.
Quickfix for that is adding the filename argument to the filter call:

from compressor.filters.css_default import CssAbsoluteFilter
from django_libsass import SassCompiler

class PatchedSCSSCompiler(SassCompiler):
    def input(self, **kwargs):
        content = super(PatchedSCSSCompiler, self).input(**kwargs)
        kwargs.setdefault('filename', self.filename)
        return CssAbsoluteFilter(content).input(**kwargs)

Thanks @jsSenanga I was just trying to track this issue down. Thanks @woeye for the original patch...
I now have this working under Local dev mode and Heroku Deployment with herokuside collectstatic and compression....


+1 for a permanent solution. In my case, I've had the problem with django-libsass, and solved it by using @jsSenanga 's solution. Thank you!

@karyon karyon added a commit to karyon/django-compressor that referenced this issue
@karyon karyon Apply CssAbsoluteFilter to precompiled css even when compression is d…

fixes #226, fixes #296
@karyon karyon added a commit to karyon/django-compressor that referenced this issue
@karyon karyon Apply CssAbsoluteFilter to precompiled css even when compression is d…

fixes #226, fixes #296
@karyon karyon added a commit to karyon/django-compressor that referenced this issue
@karyon karyon Apply CssAbsoluteFilter to precompiled css even when compression is d…

fixes #226, fixes #296
@diox diox closed this in #653

shoutout to @domoritz :) i thought it was really funny and random to see you here. (johannes linke speaking)


Haha, awesome to meet you here @karyon and thanks for working on this issue. I don't quite remember what I was working on back then but it looks like a relevant issue judging from the number of people in this thread.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.