I come from a C# MVC background but I am currently working on a PHP project.
In C# you can setup decorators to specify the request URL using something along these lines:
[HttpGet]
[Route("/ControllerName/ActionName")]
public string MyAction() {
// ...
}
I am wanting to set something similar up in PHP, so I set my file structure like this:
+ assets
+ css
+ javascript
+ images
+ server
+ api
+ controllers
- index.php
+ ui
+ area1
- view1.html
- view2.html
+ area2
- etc.html
- .htaccess
- index.html
Where index.html is my login page, the other html pages live in their respective /ui/area directory, and my restful API pages live in /server/api/controllers. The /server/api/index.php follows a very similar pattern to this Stack Overflow answer: https://stackoverflow.com/a/1737903/1920035
In my .htaccess file, I have the following redirect rule setup:
# Redirect traffic starting with /api to the /server/api/index.php file
Redirect 301 ^(api/.*)$ /server/api/index.php [L,QSA]
The idea is that if I hit /api/account/get that it will redirect to the /server/api/index.php file and run its magic to run the respective controller/action.
The issue is that when I spin up my server and hit the pretty URL that it always returns a 500 response without any useful response text.
To complicate things, I'm running Docker for Windows using php:8.2.2-apache.
What am I doing wrong here? I do not have much experience setting up .htaccess files and with a vague 500 response, I don't have much to go off of.
You need to check Apache's error log for the details of the 500 error. However...
Redirect 301 ^(api/.*)$ /server/api/index.php [L,QSA]
This is syntactically invalid (which might account for the 500 error) and the wrong directive to use here. Redirect is a mod_alias directive that triggers an external redirect. [L,QSA] are "flags" used with the mod_rewrite RewriteRule directive. The Redirect directive does not take a regex as an argument (it uses simple prefix-matching).
But you shouldn't be triggering an HTTP external (301) redirect here, you need to internally rewrite the request instead (the user should not be aware of the underlying /server subdirectory).
For this (URL rewriting) you need to use mod_rewrite (so this needs to be installed if not already). For example:
RewriteEngine On
RewriteRule ^api/ server/api/index.php [L]
A request for /api/<anything> would be internally rewritten to /server/api/index.php.
No need for the capturing group and .* suffix on the original regex. So, ^api/ is essentially the same as ^(api/.*)$, but more efficient. The slash prefix on the substitution string (2nd argument) is not required with internal rewrites (and best omitted - so it is treated as a filesystem path and not a URL-path).
mod_rewrite (RewriteRule / RewriteCond directives) is capable of issuing (complex) external redirects as well as internal rewrites. mod_alias (Redirect / RedirectMatch directives) is for simple external redirects only. (Having said that, mod_alias also has some directives used for internal filesystem mapping.)
Reference:
https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewriterule
https://httpd.apache.org/docs/current/mod/mod_alias.html#redirect
Related
I have a problem with a project I'm doing with PHP and it's in the URLs.
When I load a script like index.php everything works fine, the problem is when I load a script that is located within two or more directories.
In the URL the scripts with the routes begin to be enmeshed
Here is an example of the problem I have
I need to load a script, even if it is in several levels of nesting, make its functionality and in the url is reflected as:
I need to have something like this
1:
I thank you in advance.
Regards
You can't use PHP to achieve this. PHP is not responsible for determining if PHP (let along a particular PHP script) will handle any given URL.
You have to configure your webserver to do it. Since you mention .htaccess but provide no further information about your server, I'm going to assume you are using Apache HTTPD.
For Apache, that means using mod_rewrite, Alias or something similar. You can put the configuration for those tools in .htaccess, but you don't want to and the documentation advises not to use them.
So put your mod_rewrite or Alias configuration in the main Apache configuration.
You're going to need an htaccess rule no matter what. However, it doesn't have to be a mod_rewrite rule. The reason you need this rule is because PHP is not responsible for the routing - it is merely responsible for the execution of your script.
The point of the rule is to direct apache and instruct it to execute the right script (in your case, script32.php) while keeping the request uri as intact as possible.
There are two ways around it, basically.
Way 1 (cleaner): mod_rewrite
This is pretty straightforward, the set of rules you need are as follows:
# If the requested file name is a valid file/inode
RewriteCond %{REQUEST_FILENAME} -f [OR]
# ...or a directory
RewriteCond %{REQUEST_FILENAME} -d
# ...then throw them straight on it
RewriteRule (.*) - [L]
# ...otherwise, redirect to script32.php with the full content of the request in query string
RewriteRule (.*) /welcome/script32.php?$1 [L]
The requested URL is now in $_SERVER['QUERY_STRING'] and you can now do whatever you like with it in PHP
Way 2: catchall
This does not rely on mod_rewrite and may therefore be slightly faster. However, technically, it's a cheap hack. The way around it is as follows:
ErrorDocument 404 /welcome/script32.php
The requested URL can now be found in $_SERVER['REQUEST_URI'] and is available for parsing in PHP. However, with this, you've also disabled "legit" 404 errors from being generated through apache - and should make sure to obey proper behaviour in PHP to compensate.
After many hours messing with .htaccess I've arrived to the conclusion of sending any request to a single PHP script that would handle:
Generation of html (whatever the way, includes or dynamic)
301 Redirections with a lot more flexibility in the logic (for a dumb .htaccess-eer)
404 errors finally if the request makes no sense.
leaving in .htaccess the minimal functionality.
After some tests it seems quite feasible and from my point of view more preferable. So much that I wonder what's wrong or can go wrong with this approach?
Server performance?
In terms of SEO I don't see any issue as the procedure would be "transparent" to the bots.
The redirector.php would expect a query string consisting on the actual request.
What would be the .htaccess code to send everything there?
I prefere to move all your php files in a other directory and put only 1 php file in your htdocs path, which handle all requests. Other files, which you want to pass without php, you can place in that folder too with this htaccess:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /index.php/$0 [L]
Existing Files (JPGs,JS or what ever) are still reachable without PHP. Thats the most flexible way to realize it.
Example:
- /scripts/ # Your PHP Files
- /htdocs/index.php # HTTP reachable Path
- /htdocs/images/test.jpg # reachable without PHP
- /private_files/images/test.jpg # only reachable over a PHP script
You can use this code to redirect all requests to one file:
RewriteEngine on
RewriteRule ^.*?(\?.*)?$ myfile.php$1
Note that all requests (including stylesheets, images, ...) will be redirected as well. There are of course other possibilities (rules), but this is the one I am using and it will keep the query string correct. If you don't need it you can use
RewriteEngine on
RewriteRule ^.*?$ myfile.php
This is a common technique as the bots and even users only see their requested URL and not how it is handled internally. Server performance is not a problem at all.
Because you redirect all URLs to one php file there is no 404 page anymore, because it gets cached by your .php file. So make sure you handle invalid URLs correctly.
I am trying to enable clean urls with Angular JS inside my Laravel 4 app. When I tried the required thing normally without laravel 4 refering to this url.
http://www.yearofmoo.com/2012/11/angularjs-and-seo.html, I was able to get the thing to work. Even ?_escaped_fragment_= was changed to snapshot/* folder and picked the content.
But I am not able to do the same in Laravel 4. I am not sure how to do this.
I am trying to define a route like ?_escaped_fragment_=/* and redirect it to some controller but that doesn't work.
Can anyone please help.
Escaped fragment is a query parameter, not a route, there for, in the root route Route::get('/', 'RootCtrl');, you can check for $_GET['_escaped_fragment'] presence, and if it is there, return an HTTP 302 redirect to the corresponding file in the snapshots dir.
While this will work, it is not the perfect solution, since some bots might index the path of the snapshots file instead of the original one, and since doing it using the Laravel framework is not correct in the first place.
The best choice of course, is to rewrite any request that contains an _escaped_fragment_
query parameter to it's corresponding path, in the nginx\apache configuration, prior to the configuration of your Laravel app, this way those requests won't event reach the Laravel router.
I have co-authored an Angular SEO plug-and-play solution, using PhantomJS & Mongoose integrated web server, to pre-render any Async JS code, and server it as raw HTML.
The server configuration aspects are also explained in the README.md file, please note that if you would like to use static snapshots, just change proxy-pass to rewrite(in order for the right URL to get indexed by the bots).
Hope that helps.
Example using simple rewrites with nginx:
if ($args ~* "^_escaped_fragment_=(.+)") {
set $path $1
set $args '';
rewrite ^.*$ /snapshots/$path last;
}
EDIT:
Apache, for your request (some words of advice: move to nginx).
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{QUERY_STRING} ^_escaped_fragment=(.+)$
RewriteRule ^.*$ /snapshots/%1 [QSA,L]
</IfModule>
I've been tasked to clean up 30,000 or so url errors left behind from an old website as the result of a redesign and development.
I normally use .htaccess to do this, but I doubt it would be wise to have 30,000 301 redirects inside the .htaccess file!
What methods have some of you used to solve this problem?
Thanks in advance.
Here as you can do with apache httpd
RewriteMap escape int:escape
RewriteMap lowercase int:tolower
RewriteMap my_redir_map txt:map_rewrite.txt
RewriteCond ${my_redir_map:${lowercase:${escape:%{HTTP_HOST}%{REQUEST_URI}}}} ^(.+)$
RewriteRule .* http://%1 [R=301,L]
I use this rewrite rules usually directly inside apache httpd configuration.
Inside the map_rewrite.txt file you have a tab delimited file with the list of redirect in the following format:
www.example.it/tag/nozze www.example.it/categoria/matrimonio
www.example.it/tag/pippo www.example.it/pluto
www.example.it/tag/ancora www.google.com
Would be much easier if you can generalize the approach because the redirect have a common pattern. But if not, in this case you only need to add the redirected url into the list.
Take care to study the RewriteMap configuration, because you can also write the list into a different format, for example like a database table.
Please pay attention to this: I have added escape and lowercase only because there are accents into the urls I need to write. If your urls doesn't have accents, you can remove both.
If you want implement these redirects in php, here the code you need:
<?php
$dest_url = "http://example.com/path...";
header("HTTP/1.1 301 Moved Permanently");
header("Location: ".$dest_url);
Create a PHP page to operate as a 404 handler. It should inspect the incoming URL, check if it should map from an old page to a new page, then issue a 301. If there is no mapping then present a 404.
Simply set this page as the 404 handler in your .htaccess and there you go. IIRC this is how Wordpress used to handle 'clean' URLs on IIS before IIS7 brought in URL rewriting without needing a 3rd-party dll.
I have made a redirect class that is on the 404 page that will check the database if there is a valid page to 301 redirect to and redirect it instead of giving the 404 page. If it can't figure that out, it marks it in the database as a 404 page, so it can be fixed later.
Thanks for help guys. I've carried out the suggested course of action from freedev but have created a separate config file within Apache.
Within the httpd.conf file I have added:
# Map settings
Include "conf/extra/map.conf"
The map.conf file:
RewriteEngine On
RewriteEngine on
RewriteMap url_rewrite_map txt:conf/map.map
RewriteCond ${url_rewrite_map:$1|NOT_FOUND} !NOT_FOUND
RewriteRule ^(.*) http://website.com/${url_rewrite_map:$1} [R=301]
The map.map file is formatted as:
/oldname/ /newname
I've added quite a few of the urls for the redirection and so far so good, it isn't having a massive impact on the server like it did when added to .htaccess
I have changed the CMS to my site and it has rewritten the URLs to be more friendly but I have 20 pages which are linked on different sites across the web. I want when people click those old links for them to be redirected to their new respective posts.
I was at first thinking of using a PHP array with a javascript redirect but now I'm thinking .htaccess mod-rewrite would be best.
Whats the best way to have specific URLs redirect to their new URLs?
Use the .htaccess file for redirects. For example:
# This allows you to redirect index.html to a specific url
Redirect /index.html http://example.com/location/
Add some regex to match your rewrites and send to the dynamically created remote url. For example:
# Dynamic redirect
RedirectMatch 301 yourUrl/\(.*)\$ http://external.com/someUrl.html?dynamicVal=$1
That would send ...yourUrl/123 -> http://external.com/someUrl.html?dynamicVal=123
If you are talking about URLs on your pages, then the best way is to edit all such pages with current proper URLs.
If you are talking about "what to do when someone hits my old URL (from their bookmarks or by clicking a link on another site, for example) then the best way is to redirect (with code 301) such requests to a new location using Apache's URL Rewrite module.
Example:
# Activate Rewrite Engine
RewriteEngine On
# redirect to another page on the same site
RewriteRule ^mypage.php /other/mypage.php [R=301,L,QSA]
# redirect to another page on ANOTHER site
RewriteRule ^mypage.php http://www.anothersite.com/mypage.php [R=301,L,QSA]
Here is the link to Apache's manual for mod-rewrite.
Another useful place -- Mod_Rewrite Forums.
Apache supports different modules to rewrite/redirect requested URIs:
mod_alias with Alias and AliasMatch for internal rewriting, as well as Redirect and RedirectMatch for external redirecting
mod_rewrite with RewriteRule for internal rewriting and external redirecting
The main difference between the mod_alias and mod_rewrite directives is that mod_alias’s Alias and Redirect directives work with path prefix patterns while the other directives (i.e. AliasMatch, RedirectMatch, and RewriteRule) work with regular expressions.
A path prefix pattern is always always matched when it’s a qualified prefix of the requested path and the remaining path segments are automatically appended to the target path. Here’s an example:
Redirect /foo/bar http://other.example/quux
If /foo/bar is requested, it’s getting redirected to http://other.example/quux; likewise /foo/bar/baz is getting redirected to http://other.example/quux/baz. Alias works alike but just internally.
In opposite to that, AliasMatch, RedirectMatch, and RewriteRule work with regular expressions and can be used not just to match path prefixes. Here’s an example:
RedirectMatch ^/foo/bar$ http://other.example/quux
If /foo/bar is requested, it’s getting redirected to http://other.example/quux (just like the Redirect example above). But if /foo/bar/quux is requested, it’s not getting redirected as ^/foo/bar$ doesn’t match /foo/bar/quux.
Futher differences between mod_alias and mod_rewrite are that mod_rewrite can also examine other parts of the URI except the path and even HTTP request header fields. It does also allow more complex operations with multiple conditions.