I am hosting a website with multiple subdomains from S3 buckets through Cloudfront.
When I go to www.domain.com/subdomain/ (note: with trailing slash), the website loads correctly and fetches the minified .js and .css files from www.domain.com/subdomain/****.js.
However if I navigate to www.domain.com/subdomain without the trailing slash, the site's index.html is still served but the assets attempt to be fetched from www.domain.com/****.js.
I have tried to use a lamba#edge function to change the request uri and append the slash however that is not working. Thanks for any help!
This will be caused by the html referencing relative file paths (i.e. src="****.js" vs src="/subdomain/****.js").
If you are looking to fix this, you will need to perform a redirect to the slash path in the users browser. This can be done by using a Lambda#Edge function to perform the redirect in the Origin Response event.
An example redirect function is below
def lambda_handler(event, context):
# Generate HTTP redirect response with 302 status code and Location header.
response = {
'status': '302',
'statusDescription': 'Found',
'headers': {
'location': [{
'key': 'Location',
'value': 'http://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html'
}]
}
}
return response
For this you would need to add your custom logic to check if the URL needs to be redirected by checking for a "/" character as the last character of the request.
Additionally if you can change the path of your css, js and images from being relative to being absolute as pointed out at the top of this answer.
Related
I have an ALB with listener rules that check for path and headers, but after clearing cookie with accessToken after going to /path1, it makes it impossible to go back to that path, and automatically redirect to /home every time. For some reason, going to /path2 which is in the same set of rules as /path1, it would work, but if you clear the cookie once again, it becomes corrupted and loop you back to /home. Both /path1 and /path2 would be inaccessible to the user. I have the following listener rules:
Listener:
Rule 1:
when path = /login
when headers contain: cookie = *accessToken*
Redirect to path /home
Rule 2:
when path = /login
Redirect to path /home
Rule 3:
when path = /home
when headers contain: cookie = *accessToken*
Forward to lambda target group that checks access token and returns content type html if valid, else erase cookie and return to /login.
Rule 4:
when path = /home
Redirect to path /login
Rule 5:
when path = /path1 or /path2 or /path3
when headers contain: cookie = *accessToken*
Forward to lambda target group that checks access token and returns content type html if valid, else erase cookie and return to /login.
Rule 6:
when path = /path1 or /path2 or /path3
Forward to path /login
As far as I am aware, ALB doesn't have caching capabilities, so I thought the problem is with the browser caching that is done via html files or lambda responses so I've added to the response of lambda:
Lambda Target Group Response:
return {
statusCode: 200,
headers: {
'Content-Type': 'text/html',
'Cache-Control': 'no-cache, no-store, must-revalidate'
},
body: html
}
HTML files:
<head>
<meta charset="utf-8" />
<title>Website teste</title>
<meta http-equiv="cache-control" content="no-cache, no-store, must-revalidate">
</head>
The problem still occurs and still can't go back to the path. I tried adding to the html files headers and it still didn't work. What would be the other reasons why the path would get corrupted and unable to return to it without getting the redirect loop?
I have a url say example.com/eu/blog/ , in this page ,I have a button , when any user click on button , it goes to oil-gas.com/en (this is a different website) .. now I want this link to be as example.com/eu/blog/oil-gas/en and currently I don't have anything in example.com/eu/blog/oil-gas/en .
I have used apache proxypass and it works , but I want to know if we can do this using S3 , cloudfront .. can we create bucket in S3 and enable static hosting with redirection ... I want the url to be specifically example.com/eu/blog/oil-gas/en. Please help or advice more
You can set a redirect in CloudFront by adding a Lambda#Edge function. This would be added to the Origin Response event.
The code would look like the below
def lambda_handler(event, context):
request = event["Records"][0]["cf"]["request"]
response = event["Records"][0]["cf"]["response"]
#If it matches this path perform a redirect
if request['uri'] == "url/to/redirect":
response = {
'status': '302',
'statusDescription': 'Found',
'headers': {
'location': [{
'key': 'Location',
'value': 'http://example.com/eu/blog/oil-gas/en'
}]
}
}
return response
Here are some examples.
I am developing a ExtJS application that uses a Django-rest-framework service. I am using CORS headers to allow fetching the data from the service (https://github.com/OttoYiu/django-cors-headers).
What happens is that at a point in time I want to change the URL from the store. And when I do that I get the following error:
XMLHttpRequest cannot load http://10.98.0.241:8000/reacsearch/as?_dc=1418831884352&page=1&start=0&limit=25. The request was redirected to 'http://10.98.0.241:8000/reacsearch/as/?_dc=1418831884352&page=1&start=0&limit=25', which is disallowed for cross-origin requests that require preflight.
In the settings.oy I define the following properties for the CORS
CORS_ALLOW_METHODS = (
'GET',
'OPTIONS'
)
CORS_ORIGIN_ALLOW_ALL = True
This works fine when I use URLs to list all the elements in my database, however when I change the store for another URL I get the error above. Also the link works fine in the browser.
The store url change is made this way:
var store = Ext.getStore(storeName);
store.getProxy().setUrl(newURL);
store.load();
The difference between the views, is that the two that work on the application are viewsets, while the other is just a generic list
class Example1viewset(viewsets.ModelViewSet):
"""
API endpoing that allows metabolites to be viewed.
"""
queryset = examples1.objects.all()
serializer_class = Example1Serializer
class Example1SearchList(generics.ListAPIView):
serializer_class = Example1Serializer
def get_queryset(self):
queryset = Example.objects.all()
if 'attr' in self.kwargs:
queryset = queryset.filter(Q(attribute1__contains=self.kwargs['attr']) | Q(attribute2__contains=self.kwargs['abbr']))
return queryset
Like I mentioned both examples work fine in the browser (even accessing through other computers in the network), however in the application when changing the URL of the store I get the CORS error. Does anyone has any idea why this is happening?
Thank you.
Edit:
Just for clarification, the problem is not in changing the url of the store. As I tried to set those urls as defaults, but they are not working when accessing from the application.
My urls.py file:
router = routers.DefaultRouter()
router.register(r'example', views.Example1ViewSet)
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^reacsearch/(?P<attr>.+)/$', Example1SearchList.as_view()),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
Can it be that the problem is related with the fact that I am not adding the search list to the router?
Edit2
Problem solved since I was trying to fetch data from a different domain. I changed the type of store to jsonp in Extjs, and I also allowed my rest service to render data as jsonp.
Just a reminder if anyone comes accross this same problem, it is necessary to add ?format=jsonp to the store url:
http://my/url/?format=jsonp
Since it looks like an alternate solution was found, I'll explain what the issue appeared to be as well as why the alternative works.
XMLHttpRequest cannot load first url. The request was redirected to 'second url', which is disallowed for cross-origin requests that require preflight.
The issue here is that you are telling Django to enforce the trailing slash, which makes it automatically redirect urls without a trailing slash to urls with a trailing slash, assuming that one exists. This is why, as stated in the error, the request was redirected to the second url, which you can tell has the missing trailing slash. This is controlled by the APPEND_SLASH Django setting which is True by default.
The problem is that when CORS is doing a preflight request, which is what allows it to determine if the request can be made, there must be a valid response at the requested URL. Because you are redirecting the request, the preflight request fails and you're stuck without your information.
You can fix this by adding the trailing slash in your code. There appear to be a few solutions for doing this with ext, but I personally can't recommend a specific one. You can also manually set the url to use the trailing slash, which sounds like what you were doing previously.
Or you can use JSONP...
You've found the alternative solution, which is to use JSONP to make the request instead of relying on CORS. This gets around the preflight issue and works in all major browsers, but there are some drawbacks to consider. You can find more information on CORS vs JSONP by looking around.
You're going to need CORS if you want to push any changes to your API, as JSONP only supports GET requests. There are other advantages, such as the ability to abort requests, that also comes with CORS.
Using the Django-auth application (Django version 1.3), I want to have my login page go to https://mysite.com/login/. Currently, I'm using:
# urls.py
from django.contrib.auth.views import login
urlpatterns = patterns('', url(r'^login/$', login, name='login-view'),)
# navbar.html
<li id="nav-login"><a href="{% url login-view %}" ><b>Login</b></a></li>
which works nicely, but goes to http://mysite.com/login/.
Is there some way to tell Django-auth what prefix (https) to use, when it reverses the view name? I've read the entire manual page, and haven't found anything that covers it. Or maybe some way to tell the url tag to go to https?
Or is the only option to specify the entire URL manually? I hope not :) And given how powerful Django has been so far, I can't believe it wouldn't have that ability - I must be overlooking it. :)
Set OS environmental variable HTTPS to on
You need to enable the OS environmental variable HTTPS to 'on' so django will prepend https to fully generated links (e.g., like with HttpRedirectRequests). If you are using mod_wsgi, you can add the line:
os.environ['HTTPS'] = "on"
to your wsgi script. You can see the need for this by reading django/http/__init__.py:
def build_absolute_uri(self, location=None):
"""
Builds an absolute URI from the location and the variables available in
this request. If no location is specified, the absolute URI is built on
``request.get_full_path()``.
"""
if not location:
location = self.get_full_path()
if not absolute_http_url_re.match(location):
current_uri = '%s://%s%s' % (self.is_secure() and 'https' or 'http',
self.get_host(), self.path)
location = urljoin(current_uri, location)
return iri_to_uri(location)
def is_secure(self):
return os.environ.get("HTTPS") == "on"
Secure your cookies
In settings.py put the lines
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
and cookies will only be sent via HTTPS connections. Additionally, you probably also want SESSION_EXPIRE_AT_BROWSER_CLOSE=True. Note if you are using older versions of django (less than 1.4), there isn't a setting for secure CSRF cookies. As a quick fix, you can just have CSRF cookie be secure when the session cookie is secure (SESSION_COOKIE_SECURE=True), by editing django/middleware/csrf.py:
class CsrfViewMiddleware(object):
...
def process_response(self, request, response):
...
response.set_cookie(settings.CSRF_COOKIE_NAME,
request.META["CSRF_COOKIE"], max_age = 60 * 60 * 24 * 7 * 52,
domain=settings.CSRF_COOKIE_DOMAIN,
secure=settings.SESSION_COOKIE_SECURE or None)
Direct HTTP requests to HTTPS in the webserver
Next you want a rewrite rule that redirects http requests to https, e.g., in nginx
server {
listen 80;
rewrite ^(.*) https://$host$1 permanent;
}
Django's reverse function and url template tags only return relative links; so if you are on an https page your links will keep you on the https site.
As seen in other StackOverflow questions, you could implement middleware that would automatically redirect the login page to a secure version.
If you are really serious about security, you should probably migrate the entire website to SSL. From the EFF's How to Deploy HTTPS Correctly:
You must serve the entire application domain over HTTPS. Redirect HTTP requests with HTTP 301 or 302 responses to the equivalent HTTPS resource.
Some site operators provide only the login page over HTTPS, on the theory that only the user’s password is sensitive. These sites’ users are vulnerable to passive and active attack.
I'm writing an image bank with Django, and I want to add a button to get a hi-res version of an image (the low-res is shown in the details page). If I put just an <a> link, the browser will open the image instead of downloading it. Adding an HTTP header like:
Content-Disposition: attachment; filename="beach008.jpg"
works, but since it's an static file, I don't want to handle the request with Django. Currently, I'm using NGINX to serve static files, and dynamic pages are redirected via FastCGI to the Django process. I'm thinking about using NGINX add-header command, but could it set the filename="xx" part?. Or maybe there's some way to handle the request in Django, but make NGINX serve the content?
If your django app is proxied by nginx you can use x-accell-redirect. You need to pass a special header in your response, nginx will intercepet this and start serving the file, you can also pass Content-Disposition in the same response to force a download.
That solution is good if you want to control which users acess these files.
You can also use a configuration like this:
#files which need to be forced downloads
location /static/high_res/ {
root /project_root;
#don't ever send $request_filename in your response, it will expose your dir struct, use a quick regex hack to find just the filename
if ($request_filename ~* ^.*?/([^/]*?)$) {
set $filename $1;
}
#match images
if ($filename ~* ^.*?\.((jpg)|(png)|(gif))$) {
add_header Content-Disposition "attachment; filename=$filename";
}
}
location /static {
root /project_root;
}
This will force download on all images in some high_res folder (MEDIAROOT/high_rest). And for the other static files it will behave like normal. Please note that this is a modified quick hack that works for me. It may have security implications, so use it with precaution.
I wrote a simple decorator, for django.views.static.serve view
Which works for me perfectly.
def serve_download(view_func):
def _wrapped_view_func(request, *args, **kwargs):
response = view_func(request, *args, **kwargs)
response['Content-Type'] = 'application/octet-stream';
import os.path
response['Content-Disposition'] = 'attachment; filename="%s"' % os.path.basename(kwargs['path'])
return response
return _wrapped_view_func
Also you can play with nginx mime-types
http://wiki.codemongers.com/NginxHttpCoreModule#types
This solution didn't work for me, because I wanted to have both direct link for the file (so user can view images, for example), and download link.
What i'm doing now is to use a different URL for download than for 'views', and add the filename as an URL arg:
usual media link: http://xx.com/media/images/lores/f_123123.jpg
download link: http://xx.com/downs/hires/f_12323?beach008.jpg
and nginx has a config like this:
location /downs/ {
root /var/www/nginx-attachment;
add_header Content-Disposition 'attachment; filename="$args"';
}
but i really don't like the smell of it.