1

I have setup a simple wordrpress environment with ddev.

I am trying to use the ddev share function which should give out a live preview of the local WP site but when I try visiting the site from another computer on a different ethernet then the site loads up, all in regards to html and some primitive css but not the whole thing.

Locally ofcourse everything loads well.

In my console of the ngrok site I get various warnings like such:

Loading failed for the <script> with source “https://mysite.ddev.site:8443/wp-includes/js/jquery/jquery.min.js?ver=3.5.1”.

Note, the "mysite.ddev..." is the link to my local environment, So my guess is that this is to do with something regarding relative/abs urls, but I am unsure on how to fix this

I reffered to this post from 5 years ago which kind of touches on the subject.

I installed "Absolute Relative URLS" but that didnt solve the problem.

EricTalv
  • 1,000
  • 1
  • 13
  • 26

3 Answers3

1

The first step to get WP and ngrok playing nicely is to add this line to your .ddev/config.yaml:

ngrok_args: "--host-header=rewrite"

This should be enough to make the ddev share ngrok URL load on your phone, but with broken images and styles.

What the Local app by Flywheel then does to make this work with its Live Links is add the following (GPL-licensed) must-use plugin to wp-content/mu-plugins. It removes WP's redirects to the canonical URL, and then it replaces the local URL with the public URL on a ton of WP filter hooks:

<?php
/*
Plugin Name: Local WP Live Link Helper
Plugin URI: https://localwp.com
Description: Makes WordPress URL functions relative to play nicely with Live Links.
Version: 2.0
Author: Flywheel
Author URI: http://getflywheel.com
License: GPLv2 or later
*/

class LocalWP_Live_Link_Helper {
/**
 * host/domain parsed from 'home' option
 *
 * @var string|null
 */
public $home_domain;

public function __construct() {
    $this->home_domain = str_replace( '/^www./', '', parse_url( get_option( 'home' ), PHP_URL_HOST ) );

    $this->add_filters();
}

/**
 * @return string Host from server along with port re-added if using localhost routing mode.
 */
public function get_local_host() {
    $original = str_replace( '/^www./', '', $_SERVER['HTTP_HOST'] );

    /**
     * If using localhost and the port isn't in HTTP_HOST then it needs to be re-added.
     */
    if ( $original === 'localhost' ) {
        $original .= ':' . $_SERVER['SERVER_PORT'];
    }

    return $original;
}

/**
 * Convenience method to get tunnel domain from headers.
 *
 * @return string
 */
public function get_tunnel_host() {
    return $_SERVER['HTTP_X_ORIGINAL_HOST'];
}

/**
 * @return void
 */
public function add_filters() {
    /**
     * Do not add any of these filters if the X-Original-Host header is missing.
     */
    if ( empty( $_SERVER['HTTP_X_ORIGINAL_HOST'] ) ) {
        return;
    }

    add_action( 'send_headers', array( $this, 'send_private_cache_control_header' ), 9999 );
    add_action('send_headers', array($this, 'send_local_host_header'), 9999);

    remove_action( 'template_redirect', 'redirect_canonical' );

    $local_to_tunnel_filters = array(
        'get_rest_url',
        'wp_redirect',
        'bloginfo_url',
        'the_permalink',
        'wp_list_pages',
        'wp_list_categories',
        'the_content_more_link',
        'the_content',
        'the_tags',
        'the_author_posts_link',
        'post_link',
        'post_type_link',
        'page_link',
        'attachment_link',
        'get_shortlink',
        'post_type_archive_link',
        'get_pagenum_link',
        'get_comments_pagenum_link',
        'term_link',
        'search_link',
        'day_link',
        'month_link',
        'year_link',
        'option_siteurl',
        'blog_option_siteurl',
        'option_home',
        'admin_url',
        'get_admin_url',
        'get_site_url',
        'network_admin_url',
        'home_url',
        'includes_url',
        'site_url',
        'site_option_siteurl',
        'network_home_url',
        'network_site_url',
        'get_the_author_url',
        'get_comment_link',
        'wp_get_attachment_image_src',
        'wp_get_attachment_thumb_url',
        'wp_get_attachment_url',
        'wp_login_url',
        'wp_logout_url',
        'wp_lostpassword_url',
        'get_stylesheet_uri',
        'get_locale_stylesheet_uri',
        'script_loader_src',
        'style_loader_src',
        'get_theme_root_uri',
        'theme_root_uri',
        'plugins_url',
        'stylesheet_directory_uri',
        'template_directory_uri'
    );

    foreach ( $local_to_tunnel_filters as $local_to_tunnel_filter ) {
        add_filter( $local_to_tunnel_filter, array( $this, 'make_link_tunnel' ) );
    }

    add_filter( 'pre_update_option', array( $this, 'make_link_local' ) );
    add_filter( 'wp_insert_post_data', array( $this, 'make_link_local_in_posts' ), 9999 );
}

/**
 * Prevent possible cache poisoning by sending Cache-Control private header whenever the domain is replaced.
 *
 * @return void
 */
public function send_private_cache_control_header() {
    header( 'Cache-Control: private' );
}

/**
 * Send original domain so the tunnel server can perform a replacement with it.
 */
public function send_local_host_header()
{
    header('X-Local-Host: ' . $this->get_local_host());
}

/**
 * Convenience method for replacing old host with new host.
 *
 * @param string $old Host to be replaced
 * @param string $new Host to use as replacement
 * @param string $subject Replacement subject
 */
public function replace_host( $old, $new, $subject ) {
    $subject = str_replace( 'www.' . $old, $new, $subject );
    $subject = str_replace( $old, $new, $subject );

    return $subject;
}

/**
 * Generic replacement of the site's local hostname to the tunnel hostname
 *
 * @param string $str String provided by the filter
 *
 * @return string String with local hostname replaced with the tunnel hostname
 */
public function make_link_tunnel( $str ) {
    $local_host = $this->get_local_host();
    $tunnel_host = $this->get_tunnel_host();

    $str = $this->replace_host( $local_host, $tunnel_host, $str );
    $str = $this->replace_host( $this->home_domain, $tunnel_host, $str );

    /**
     * Force HTTPS, but not for local dev testing setup ($tunnel_host ends with tunnel.testing:<port> )
     */
    if (!preg_match('/^.*tunnel\.testing:\d+$/i', $tunnel_host)) {
        $str = str_replace( 'http://' . $tunnel_host, 'https://' . $tunnel_host, $str );
    }

    return $str;
}

/**
 * Generic replacement of the site's tunnel hostname to the local hostname,
 * used when saving options to the database via the pre_update_option filter hook
 *
 * @param mixed $option Option being saved, provided by the filter. Will
 *                      usually be a serialized string, but may be an
 *                      unserialized type in some cases. See:
 *                      https://developer.wordpress.org/reference/functions/get_option/
 */
public function make_link_local( $option ) {
    if (gettype($option) !== 'string') {
        $old_str = serialize($option);
        $new_str = $this->replace_host( $this->get_tunnel_host(), $this->home_domain, $old_str );
        return unserialize($new_str);
    }

    return $this->replace_host( $this->get_tunnel_host(), $this->home_domain, $option );
}

/**
 * Go through post properties and replace the tunnel host with the local host
 *
 * @param WP_Post $post
 */
public function make_link_local_in_posts( $post ) {
    $post['post_content'] = $this->make_link_local( $post['post_content'] );

    return $post;
}
}

new LocalWP_Live_Link_Helper();

I have added the the_content filter to the big list of filters in there so it will rewrite image URLs, which I found wasn't always working with the original plugin; Local may have some additional secret sauce involving media, or I may have just done something wrong.

75th Trombone
  • 1,364
  • 15
  • 28
0

The problem with WordPress is that it embeds static URLs right into the database, so it's far harder to use in situations where you either change the hostname or do anything else. This is a significant problem with WordPress, and there are many workarounds, but not very satisfactory ones.

The article DDEV share: Sharing a DDEV-Local project with other collaborators in real time explains how to use a stable subdomain and do a search-replace. This is also updated in the DDEV docs

WordPress only has the one base URL, but the wp command is built into DDEV-Local’s web container.

This set of steps assumes an ngrok subdomain of “wp23” and a starting URL of “https://wordpress.ddev.site“.

  1. Configure .ddev/config.yaml to use a custom subdomain: ngrok_args: --subdomain wp23
  2. Make a backup of your database with ddev export-db or ddev shapshot
  3. Edit wp-config-ddev.php (or whatever your config is) to change WP_HOME, for example, define('WP_HOME', 'https://wp23.ngrok.io'); ddev ssh
  4. wp search-replace https://wordpress.ddev.site https://wp23.ngrok.io (assuming your project is configured for https://wordpress.ddev.site and your ngrok_args are configured for the wp23 subdomain) Now ddev share

Don't forget to see @75th-trombone answer below and https://stackoverflow.com/a/68551017/215713 for possible simplifications.

rfay
  • 9,963
  • 1
  • 47
  • 89
  • I have tried this solution but unfortunately to do this you'll have to submit for a paid plan, I would prefer a free solution. I know Ive used local by flywheel and they have seemed to solve the problem with ngrok links and Wordpress, but I cant say for sure if that can go with my site. I wonder if there's anything else I could do. – EricTalv May 12 '21 at 16:37
  • Would love to hear what you find in flywheel, a great service. Unfortunately, this is a basic flaw in WP. – rfay May 12 '21 at 22:53
0

After a bit experimentation I've found a solution.

This post is a bit old thus DDEV has added some additional upgrades to it's Wordpress Environment, this solution is tested on:

ddev -v:
ddev version v1.16.7

Solution:

  1. Install and Activate Relative-Url Plugin

  2. For good measures, restart the DDEV site ddev restart

  3. Into the terminal Write:

    ngrok http -host-header=rewrite .ddev.site

If you're not sure what your projects/site name is, use

ddev describe

and underneath the URLS the first link that will lead you to your localhost site should be the URL you insert into your ngrok rewrite

EricTalv
  • 1,000
  • 1
  • 13
  • 26