My clients old site was running through Wix.
Wix creates AJAX permalinks like this: old-domain.com/#!about/c20r9 instead of this: old-domain.com/about
I have developed a new WordPress site for them via new-domain.com. old-domain.com has been added to new-domain.com as a domain alias.
I would like to re-direct old page urls from old-domain.com to specific pages on new-domain.com. For example: old-domain.com/#!about/c20r9 should re-direct to: new-domain.com/about.
I understand that 301 redirect rules do not work as the server will not recognise hash urls.
How can I re-direct the old URLs for old-domain.com to new-domain.com?
As mentioned in my comment, JavaScript is the way to go as the fragment (#...) is never sent to the server.
In your specific case, you could use the following JS code to redirect your page accordingly.
Automatic mode: Use if the page names have not changed:
// Get the current hash
var currentHash = window.location.hash;
// If there is one, and it is in fact a hashbang, redirect it to the new URI.
// This extracts the 'about' from '#!about/c20r9', for example, and assigns the
// location (/about) once extracted.
if (currentHash.indexOf('#!') == 0){
window.location.assign(currentHash.replace(/^#!([^\/]+)\/.*/, '$1'));
}
Manual mode: Use if your page names differ (when comparing Wix to your migrated site). This method maps the redirects using an object, scans the object, and redirects if a match is found.
// Get the current hash
var currentHash = window.location.hash;
// If there is one, and it is in fact a hashbang, redirect it to the new URI,
// based on the array set out.
if (currentHash.indexOf('#!') == 0) {
// Get the old page name from the hash
var oldPageHash = currentHash.replace(/^#!([^\/]+)\/.*/, '$1');
// Define the redirects (old and new counterparts)
var redirects = {
'foo-page': 'new-foo-page',
'bar-page': 'new-bar-page',
}
// Loop through the redirects and set the location if there is a match
for (var oldPage in redirects) {
if (redirects.hasOwnProperty(oldPage)) {
if (oldPage == oldPageHash) {
window.location.assign(redirects[oldPage]);
}
}
}
}
Related
I am trying to get URL which I am getting after redirection if do following:
cy.url().then(url => {
cy.log(url);
});
Then I get logged initial URL, but not the (new url), how do I get url in cypress after redirection?
redirection img (new url)
Solution:
To get the redirected URL just simply add cy.get and your element so it will wait until the redirect page loads. Here is the working solution for me:
cy.get('.crumb > p')
cy.url().then(url => {
cy.log(url);
});
Exactly as you wrote, you need to first wait for a element from the new URL to be visible, using a cy.get('any-element-from-the-new-url').
After that you can get the new URL just using cy.url(), in this case if you want to log this URL you can just use cy.log(cy.url()).
A more meaningful approach would be to add an assertion on the pathname to what you are expecting. The docs can shed some light on the cy.location(). You can use an include assertion on the pathname in the situation that you will not know all of the url string.
// visiting url - https://website.com/favorites
// redirects to - https://website.com/favorites/regional/local
cy.location('pathname') // yields "/favorites/regional/local"
.should('include', '/favorites/')
.then(cy.log)
If you do this, you don't have to do any previous step. Especially because the expectedUrl doesn't have to match the actual url which allow your test doesn't crash if that happens.
cy.url('match', 'expectedUrl').then(el=>{
cy.log(el)
})
A client that follows redirects can be created as follows:
WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(
HttpClient.create().followRedirect(true)
))
After invoking a HEAD request on a URL, how can the final Location header be retrieved? In other words, how can we get the final URL redirected to?
It is true that HttpClient#followRedirect(true) enables the redirection.
However there is also HttpClient#followRedirect(BiPredicate<HttpClientRequest,HttpClientResponse>), here you can control more precisely when you want to redirect and in addition to this you have always access to the response and the Location header, so in any time you will know to which location there will be a redirection.
More info here and here
For example
WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(
HttpClient.create().followRedirect((req, res) -> {
System.out.println(res.responseHeaders().get("Location"));
return HttpResponseStatus.FOUND.equals(res.status());
})
))
I have a static website on aws s3. Have setup route 53 and cloud front and everything works smoothly. s3 Bucket is setup to serve index.html as index document.
Now I have added another file called index-en.html that should be served when the request country is any other country and not my home country.
For this I have added a lambda#edge function with the following code:
'use strict';
/* This is an origin request function */
exports.handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
const headers = request.headers;
/*
* Based on the value of the CloudFront-Viewer-Country header, generate an
* HTTP status code 302 (Redirect) response, and return a country-specific
* URL in the Location header.
* NOTE: 1. You must configure your distribution to cache based on the
* CloudFront-Viewer-Country header. For more information, see
* http://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
* 2. CloudFront adds the CloudFront-Viewer-Country header after the viewer
* request event. To use this example, you must create a trigger for the
* origin request event.
*/
let url = 'prochoice.com.tr';
if (headers['cloudfront-viewer-country']) {
const countryCode = headers['cloudfront-viewer-country'][0].value;
if (countryCode === 'TR') {
url = 'prochoice.com.tr';
} else {
url = 'prochoice.com.tr/index-en.html';
}
}
const response = {
status: '302',
statusDescription: 'Found',
headers: {
location: [{
key: 'Location',
value: url,
}],
},
};
callback(null, response);
};
I have also edited cloud front behavior to whitelist Origin and Viewer-country headers and setup the cloudfront Viewer-Request event and lambda Function ARN relation.
However I get a "too many redirect error".
I have 2 questions:
How to correct the "too many redirects error"?
For viewers outside "TR" the default landing page should be index-en.html, from which 2 more pages in english are accessible via navigation menu. So when users request a specific page from page navigation they should be able to access those pages, when no page is requested the default landing page should be served.
Appreciate help.
Thanks.
You are creating a redirect loop because you are sending the viewer back to the same site, same page, no matter what the results of your test.
if (countryCode === 'TR') {
return callback(null, request);
} else {
...
callback(null,request) tells CloudFront to continue processing the request -- not generate a response. Using return before the callback causes the rest of the trigger code not to run.
I'm developping an angular2 application (single page application). My page is never "reloaded", but it's content changes according to user interactions.
I'm having some cache problems especially with images.
Context :
My page contains an editable image list :
<ul>
<li><img src="myImageController/1">Edit</li>
<li><img src="myImageController/2">Edit</li>
<li><img src="myImageController/3">Edit</li>
</ul>
When i want to edit an image (Edit link), my dom content is completly changed to show another angular component with a fileupload component.
The myImageController returns the LastModified header, and cache-control : no-cache and must-revalidate.
After a refresh (hit F5), my page does a request to get all img src, which is correct : if image has been modified, it is downloaded, if not, i just get a 304 which is fine.
Note : my images are stored in database as blob fields.
Problem :
When my page content is dynamically reloaded with my single page app, containing img tags, the browser do not call a GET http request, but immediatly take image from cache. I assume this a browser optimization to avoid getting the same resource on the same page multiple times.
Wrong solutions :
The first solution is to add something like ?time=(new Date()).getTime() to generate unique urls and avoid browser cache. This won't send the If-Modified-Since header in the request, and i will download my image every time completly.
Do a "real" refresh : the first page load in angular apps is quite slow, and i don't to refresh all.
Tests
To simplify the problem, i trying to create a static html page containing 3 images with the exact same link to my controller : /myImageController/1. With the chrome developper tool, i can see that only one get request is called. If i manage to get mulitple server calls in this case, it would probably solve my problem.
Thank you for your help.
5th version of HTML specification describes this behavior. Browser may reuse images regardless of cache related HTTP headers. Check this answer for more information. You probably need to use XMLHttpRequest and blobs. In this case you also need to consider Same-origin policy.
You can use following function to make sure user agent performs every request:
var downloadImage = function ( imgNode, url ) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.responseType = "blob";
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200 || xhr.status == 304) {
var blobUrl = URL.createObjectURL(xhr.response);
imgNode.src = blobUrl;
// You can also use imgNode.onload callback to release blob resources.
setTimeout(function () {
URL.revokeObjectURL(blobUrl);
}, 1000);
}
}
};
xhr.send();
};
For more information check New Tricks in XMLHttpRequest2 article by Eric Bidelman, Working with files in JavaScript, Part 4: Object URLs article by Nicholas C. Zakas and URL.createObjectURL() MDN page and Same-origin policy MDN page.
You can use the random ID trick. This changes the URL so that the browser reloads the image. Not that this can be done in the query parameters to force a full cache break or in the hash to allow the browser to re-validate the image from the cache (and avoid re-downloading it if unchanged).
function reloadWithCache(img: HTMLImageElement, url: string) {
img.src = url.replace(/#.*/, "") + "#" + Math.random();
}
function reloadBypassCache(img: HTMLImageElement, url: string) {
let sep = img.indexOf("?") == -1? "?" : "&";
img.src = url + sep + "nocache=" + Math.random()
}
Note that if you are using reloadBypassCache regularly you are better off fixing your cache headers. This function will always hit your origin server leading to higher running costs and making CDNs ineffective.
I've got Varnish (3.0.3) sitting as a load-balancer/static cache in front of two web servers. I've got a CDN set up using Original Pull method. If I grab a URL from an image on my site manually, drop in the CDN address, I can verify that original pull is working and the image is pulled to the CDN and served.
My application is fairly complex and I'm testing this CDN to see if it significantly speeds up the web app, so I don't want to rewrite any of my php code to use the CDN images just yet.
What I'd like to do is set Varnish up to rewrite requests received for image files and pull them through the CDN instead of from the two Apache servers directly in my cluster.
I've read through the Varnish documentation and a couple howto's online about doing something similar, but I just can't get it to work properly and need a little help here.
Here are a couple different ways I tried doing this (edited for brevity):
sub vcl_recv {
#if request is image, redirect to CDN
if (req.url ~ "\.(gif|ico|jpg|jpeg|png)$") {
set req.http.host = "cdn.domain.com/";
error 750 req.http.host + req.url;
}
}
sub vcl_error {
if (obj.status == 750) {
set obj.status = 302;
set obj.http.Location = obj.response;
return(deliver);
}
}
That didn't work. It resulted in broken images everywhere, and anything that did show up was using the .webp extension, so it wasn't being processed by the condition above.
So I tried this:
backend cdn {
.host = "cdn.domain.com";
.port = "80";
}
sub vcl_recv {
#if request is image, redirect to CDN
if (req.url ~ "\.(gif|ico|jpg|jpeg|png)$") {
set req.backend = cdn;
return(lookup);
}
}
This showed some images on the page, but when viewing their source, they looked to be coming from the Apache servers (the domain name wasn't that of the CDN) and only about half the images were displaying...probably browser cache.
I'd love some input here, thanks guys.
Is there no way to use Varnish for this kind of redirect? Would I be better off setting nginx up in front of Varnish to rewrite requests to the cdn?
UPDATE:
Using both answers given below, I have the redirect working and an ACL in place to allow the CDN to pull images directly vs redirecting to itself. However, though I verified the ACL is allowing connection through by using my own external IP, the CDN isn't pulling new images from the server. It gives a 502 error (odd<) instead of pulling the image from the local server to the CDN and serving it. This is what the block of my vcl_recv looks like now:
acl cdn {
"ip.of.CDN";
}
sub vcl_recv {
#if request is image, redirect to CDN
if (req.url ~ "\.(gif|ico|jpg|jpeg|png)$") {
if(!client.ip ~ cdn){
error 750 "http://cdn.domain.com" + req.url;
}
}
}
sub vcl_error {
if (obj.status == 750) {
set obj.status = 302;
set obj.http.Location = obj.response;
return(deliver);
}
}
You can definitely do this with Varnish quite easily - no need to setup nginx or anything. Actually your first solution is very close to doing the trick. It just needs a few modifications.
sub vcl_recv {
#if request is image, redirect to CDN
if (req.url ~ "\.(gif|ico|jpg|jpeg|png)$") {
error 750 "http://cdn.domain.com" + req.url;
}
}
sub vcl_error {
if (obj.status == 750) {
set obj.status = 302;
set obj.http.Location = obj.response;
return(deliver);
}
}
You forgot "http://" from your CDN URL, and you can omit the last slash from the host as all req.urls begin with /.
You also need to make sure that the vcl_error code is the first one that is run in vcl_error(). I.e. if you have multiple definitions of vcl_error, make sure that none of them get to deliver any output before the if (obj.status == 750) check is reached.
Bear in mind that this solution causes all client browsers to query your server first and then make another request to the CDN after the 302 redirect. This adds a significant delay to each image load, and is probably not the best way of determining if CDN improves your app performance.
Update: Regarding your problems with CDN showing 502 errors when trying to pull content from your origin. Relying on the remote IP address for determining the redirection is quite risky, as the CDN could very well use a number of servers to do the pull, and the addresses could change over time. That would make the VCL very laborious and error-prone to maintain.
Would it be possible setting up a unique virtual host for the CDN to use? For instance originpull.domain.com and setup the CDN so that it pulls content from that address instead of your primary www.domain.com address?
You could then modify the vcl_recv() as follows:
sub vcl_recv {
#if request is image and request is not made from CDN, redirect to CDN
if (req.http.host != "originpull.domain.com" &&
req.url ~ "\.(gif|ico|jpg|jpeg|png)$") {
error 750 "http://cdn.domain.com" + req.url;
}
}
That would ensure that the requests from CDN will never be redirected.
Assuming you have the CDN pulling it's copy of the images from the site, and your not manually pushing images to the CDN. Aren't you missing a simple exclusion of the CDN network, from either your rewrite, or backend proxy? As the CDN needs to be able to directly pull a copy of the images, from your site to populate it's caches.
Been a while since I played with Varnish, and never an expert, but something along the following lines may work:
# Defnine the IP ranges of the CDN server.
acl cdn {
"localhost";
"11.22.33.0"/24;
}
...
#if request is image, redirect to CDN, unless from the CDN
if (req.url ~ "\.(gif|ico|jpg|jpeg|png)$") {
if (!client.ip ~ cdn) {
error 750 "http://cdn.domain.com" + req.url;
}
}
...