Moodle

Errors with Moodle behind a reverse proxy

Details

  • Type: Bug Bug
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Fixed
  • Affects Version/s: 1.6.4
  • Fix Version/s: 2.0
  • Component/s: Course
  • Labels:
    None
  • Environment:
    Server: Linux with Apache 2.2
  • Affected Branches:
    MOODLE_16_STABLE
  • Fixed Branches:
    MOODLE_20_STABLE

Description

We use moodle behind an Apache web server configured with mod_proxy and we get error in some elements (links, styles, ...) in some pages.
The internal and external name of the published application is not the same so we have the following configuration:

in the backend web server with Moodle 1.6.4 and Apache 2.0 we have:
in config.php
$CFG->wwwroot = "http://external_public_name/estudios";

in the frontend web server with Apache 2.2 we have:
in httpd.conf
ProxyPass /estudios http://internal_server_name/moodle
ProxyPassReverse /estudios http://internal_server_name/moodle

When we login into Moodle and go to a course, in the Topic outline screen, for example
http://external_public_name/estudios/course/view.php?id=2

the central block styles are not aplied.

The reason is that body class is not well formed.
If we see the source code of the generated page we see over line 65

<body class="http:-internal_servere_namecourse course-2" id="http:-internal_server_namecourse-view">

I supose the correct one must be <body class="course course-2" id="course-view">

This is only one error, we have detected more of the same type in the docs link in the footer of pages and in the nav bar of archives page.

We have already tested it in Moodle 1.7 and 1.8 and the same errors ocurred.

Regards

Rafael

Issue Links

Activity

Hide
Petr Škoda (skodak) added a comment -

You can not use two URLs for one Moodle instance, you can find more info here: http://docs.moodle.org/en/masquerading

Show
Petr Škoda (skodak) added a comment - You can not use two URLs for one Moodle instance, you can find more info here: http://docs.moodle.org/en/masquerading
Hide
Rafael del Aguila added a comment -

I have read http://docs.moodle.org/en/masquerading
This article explain that "You can not use internal ip address or internal server name in config.php if you want to access the server from Internet too. If you want to use Moodle server from Internet, use real DNS hostname in $CFG->wwwroot."
If you re-read my first post I use real DNS hostname in my config.php

$CFG->wwwroot = "http://external_public_name/estudios ";

The problem arise when url published in the reverse proxy [ /estudios ] is not the same that the url published in the internal server [ /moodle ]

Beyond this error, I think this limitation has no sense.
Why need Moodle work with absolute URLs?
Relative URLs are much more flexible than absolute ones and allow a correct work behind reverse proxy.
It must be an imperative improvement.

In addition, almost all pages work fine in my configuration, only some errors are displayed in some pages where absolute urls are bad used.

I think the errors come from the function me() and/or qualified_me() in weblib.php

Show
Rafael del Aguila added a comment - I have read http://docs.moodle.org/en/masquerading This article explain that "You can not use internal ip address or internal server name in config.php if you want to access the server from Internet too. If you want to use Moodle server from Internet, use real DNS hostname in $CFG->wwwroot." If you re-read my first post I use real DNS hostname in my config.php $CFG->wwwroot = "http://external_public_name/estudios "; The problem arise when url published in the reverse proxy [ /estudios ] is not the same that the url published in the internal server [ /moodle ] Beyond this error, I think this limitation has no sense. Why need Moodle work with absolute URLs? Relative URLs are much more flexible than absolute ones and allow a correct work behind reverse proxy. It must be an imperative improvement. In addition, almost all pages work fine in my configuration, only some errors are displayed in some pages where absolute urls are bad used. I think the errors come from the function me() and/or qualified_me() in weblib.php
Hide
Petr Škoda (skodak) added a comment -

The main reason why there are absolute urls is that people copy/pase urls from address bar. Decision to use absolute urls was done long ago and I am sure it will not be changed. If you want more info, please search the forums.

Show
Petr Škoda (skodak) added a comment - The main reason why there are absolute urls is that people copy/pase urls from address bar. Decision to use absolute urls was done long ago and I am sure it will not be changed. If you want more info, please search the forums.
Hide
Joacim Breiler added a comment - - edited

We are running moodle on a server at "http://localserver:8088/" and a reverse proxy (pound - http://www.apsis.ch/pound/).
Pound handles all calls to "https://example.com/moodle" and directs the traffic to to the local server.

We got the same symptoms as described above by Rafael. The module chat wouldn't even load, giving a blank page...

We solved the issue (we havn't noticed any side effects yet) by modifing the function qualified_me() in /lib/weblib.php.

The function parses the URL from $CFG->wwwroot and uses this information together with information from $_SERVER
to build an absolute url.

Replace the function qualified_me() with the one below, and set "$CFG->using_reverseproxy = true" in your config.php.

function qualified_me() {

global $CFG;

$result = $CFG->wwwroot . me();

if( !$CFG->using_reverseproxy )
{
if (!empty($CFG->wwwroot)) { $url = parse_url($CFG->wwwroot); }

if (!empty($url['host'])) { $hostname = $url['host']; } else if (!empty($_SERVER['SERVER_NAME'])) { $hostname = $_SERVER['SERVER_NAME']; } else if (!empty($_ENV['SERVER_NAME'])) { $hostname = $_ENV['SERVER_NAME']; } else if (!empty($_SERVER['HTTP_HOST'])) { $hostname = $_SERVER['HTTP_HOST']; } else if (!empty($_ENV['HTTP_HOST'])) { $hostname = $_ENV['HTTP_HOST']; } else { notify('Warning: could not find the name of this server!'); return false; }

if (!empty($url['port'])) { $hostname .= ':'.$url['port']; } else if (!empty($_SERVER['SERVER_PORT'])) {
if ($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443 ) { $hostname .= ':'.$_SERVER['SERVER_PORT']; }
}

if (isset($_SERVER['HTTPS'])) { $protocol = ($_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://'; } else if (isset($_SERVER['SERVER_PORT'])) { # Apache2 does not export $_SERVER['HTTPS'] $protocol = ($_SERVER['SERVER_PORT'] == '443') ? 'https://' : 'http://'; } else { $protocol = 'http://'; }

$url_prefix = $protocol.$hostname;
$result = $url_prefix . me();
}

return $result;
}

Show
Joacim Breiler added a comment - - edited We are running moodle on a server at "http://localserver:8088/" and a reverse proxy (pound - http://www.apsis.ch/pound/). Pound handles all calls to "https://example.com/moodle" and directs the traffic to to the local server. We got the same symptoms as described above by Rafael. The module chat wouldn't even load, giving a blank page... We solved the issue (we havn't noticed any side effects yet) by modifing the function qualified_me() in /lib/weblib.php. The function parses the URL from $CFG->wwwroot and uses this information together with information from $_SERVER to build an absolute url. Replace the function qualified_me() with the one below, and set "$CFG->using_reverseproxy = true" in your config.php. function qualified_me() { global $CFG; $result = $CFG->wwwroot . me(); if( !$CFG->using_reverseproxy ) { if (!empty($CFG->wwwroot)) { $url = parse_url($CFG->wwwroot); } if (!empty($url['host'])) { $hostname = $url['host']; } else if (!empty($_SERVER['SERVER_NAME'])) { $hostname = $_SERVER['SERVER_NAME']; } else if (!empty($_ENV['SERVER_NAME'])) { $hostname = $_ENV['SERVER_NAME']; } else if (!empty($_SERVER['HTTP_HOST'])) { $hostname = $_SERVER['HTTP_HOST']; } else if (!empty($_ENV['HTTP_HOST'])) { $hostname = $_ENV['HTTP_HOST']; } else { notify('Warning: could not find the name of this server!'); return false; } if (!empty($url['port'])) { $hostname .= ':'.$url['port']; } else if (!empty($_SERVER['SERVER_PORT'])) { if ($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443 ) { $hostname .= ':'.$_SERVER['SERVER_PORT']; } } if (isset($_SERVER['HTTPS'])) { $protocol = ($_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://'; } else if (isset($_SERVER['SERVER_PORT'])) { # Apache2 does not export $_SERVER['HTTPS'] $protocol = ($_SERVER['SERVER_PORT'] == '443') ? 'https://' : 'http://'; } else { $protocol = 'http://'; } $url_prefix = $protocol.$hostname; $result = $url_prefix . me(); } return $result; }
Hide
Carlos Alexandre S. da Fonseca added a comment - - edited

I have the same problem, but there is when i try to modify a profile, the action url in the form point to non existem address
because of qualified_me() end me() functions

my solutions was put de same folder name in the moodle application

for example:

ProxyPass /estudios http://internal_server_name/moodle
ProxyPassReverse /estudios http://internal_server_name/moodle

i change to :

ProxyPass /estudios http://internal_server_name/estudios
ProxyPassReverse /estudios http://internal_server_name/estudios

and works, but i think the moodle don't need to have this limitation

the problem its the me() function takes varieable from $_SERVER,
and takes the worng name of moodle folder , in this case, returns /moodle/any_thing.php

so the URL http://external_public_name/moodle/any_thing.php don't exist
( this is retuned from qualified_my() )

if the me() function only returns de script name (any_thing.php), the qualified_me function
could be

$result = $CFG->wwwroot .'/'. me();

PS: i'm using moodle 1.8 with this new formslib

Show
Carlos Alexandre S. da Fonseca added a comment - - edited I have the same problem, but there is when i try to modify a profile, the action url in the form point to non existem address because of qualified_me() end me() functions my solutions was put de same folder name in the moodle application for example: ProxyPass /estudios http://internal_server_name/moodle ProxyPassReverse /estudios http://internal_server_name/moodle i change to : ProxyPass /estudios http://internal_server_name/estudios ProxyPassReverse /estudios http://internal_server_name/estudios and works, but i think the moodle don't need to have this limitation the problem its the me() function takes varieable from $_SERVER, and takes the worng name of moodle folder , in this case, returns /moodle/any_thing.php so the URL http://external_public_name/moodle/any_thing.php don't exist ( this is retuned from qualified_my() ) if the me() function only returns de script name (any_thing.php), the qualified_me function could be $result = $CFG->wwwroot .'/'. me(); PS: i'm using moodle 1.8 with this new formslib
Hide
Jason Dilworth added a comment -

I've been having what I think is the same problem, but using MS ISA 2006 ;/ as the reverse proxy server.
The solution on that system was to put in a couple of Link Translations in the 'Firewall Policy' area as follows:

replace <form action="http://(internal-name)/user/editadvanced.php" method="post"
with <form action="https://(external-name)/user/editadvanced.php" method="post"
replace <form action="http://(internal-name)/blog/edit.php" method="post"
with <form action="https://(external-name)/blog/edit.php" method="post"

These were the only 2 pages that were causing problems in version 1.8.4 - user profile and blog editing pages.

Show
Jason Dilworth added a comment - I've been having what I think is the same problem, but using MS ISA 2006 ;/ as the reverse proxy server. The solution on that system was to put in a couple of Link Translations in the 'Firewall Policy' area as follows: replace <form action="http://(internal-name)/user/editadvanced.php" method="post" with <form action="https://(external-name)/user/editadvanced.php" method="post" replace <form action="http://(internal-name)/blog/edit.php" method="post" with <form action="https://(external-name)/blog/edit.php" method="post" These were the only 2 pages that were causing problems in version 1.8.4 - user profile and blog editing pages.
Hide
James Mitchell added a comment -

My simplier solution

open weblib.php find me() and replace

function me() {

with

function me() { return str_replace('/somefolder','',notreal_me()); }
function notreal_me() {

This renames me() to notreal_me() and then creates a new me() which replaces /somefolder with nothing..

e.g. if your reverse proxy is from xxx.com/ to yyy.com/~xxx then you replace /somefolder with /~xxx

You'll need to do this after every update of moodle you do..

Show
James Mitchell added a comment - My simplier solution open weblib.php find me() and replace function me() { with function me() { return str_replace('/somefolder','',notreal_me()); } function notreal_me() { This renames me() to notreal_me() and then creates a new me() which replaces /somefolder with nothing.. e.g. if your reverse proxy is from xxx.com/ to yyy.com/~xxx then you replace /somefolder with /~xxx You'll need to do this after every update of moodle you do..
Hide
Petr Škoda (skodak) added a comment -

reopening

Show
Petr Škoda (skodak) added a comment - reopening
Hide
Petr Škoda (skodak) added a comment -

Reverse proxies are now supported, but the "one address" limitation will not be removed. I can be enabled in config.php - see config-dist.php
I think it could be used probably in some load balancing configurations.

thanks for the report, please test and reopen if needed!

Show
Petr Škoda (skodak) added a comment - Reverse proxies are now supported, but the "one address" limitation will not be removed. I can be enabled in config.php - see config-dist.php I think it could be used probably in some load balancing configurations. thanks for the report, please test and reopen if needed!
Hide
Elvira Nieto added a comment -

Hello,

my solution is the next:

File: /lib/formslib.php
Function: moodleform()

The code must be:

function moodleform($action=null, $customdata=null, $method='post', $target='', $attributes=null) {
if (empty($action)){ # @author: Elvira Nieto <aquesabenlasnubes@gmail.com> # $action = strip_querystring(qualified_me()); #Change it by: $action = basename($_SERVER['SCRIPT_NAME']); # The rest code will not change ..... }

Not forget that $CFG->wwwroot variable must be configured in config.php

Best Regards

Show
Elvira Nieto added a comment - Hello, my solution is the next: File: /lib/formslib.php Function: moodleform() The code must be: function moodleform($action=null, $customdata=null, $method='post', $target='', $attributes=null) { if (empty($action)){ # @author: Elvira Nieto <aquesabenlasnubes@gmail.com> # $action = strip_querystring(qualified_me()); #Change it by: $action = basename($_SERVER['SCRIPT_NAME']); # The rest code will not change ..... } Not forget that $CFG->wwwroot variable must be configured in config.php Best Regards
Hide
David Puente Bautista added a comment -

Is there any "official answer" to this topic for 1.9.8 release? It seems to be unanswered until we have Moodle 2.0. Thanks!!!

Show
David Puente Bautista added a comment - Is there any "official answer" to this topic for 1.9.8 release? It seems to be unanswered until we have Moodle 2.0. Thanks!!!
Hide
Petr Škoda (skodak) added a comment -

no fix is planned for 1.9.x because the required changes might create serious regressions and also it would be relative difficult/time consuming, sorry

Show
Petr Škoda (skodak) added a comment - no fix is planned for 1.9.x because the required changes might create serious regressions and also it would be relative difficult/time consuming, sorry
Hide
Petr Škoda (skodak) added a comment -

reclosing

Show
Petr Škoda (skodak) added a comment - reclosing

Dates

  • Created:
    Updated:
    Resolved: